Postgres 死锁检测器并不总是有效?

Postgres deadlock detector not always working?

我们最近在 Posgres 9.4 中遇到了一些死锁问题。 有些查询根本不会返回,只会无限期地挂起。 经过一些调查,我们发现进程死锁似乎是问题所在。当 运行 来自 Lock Monitoring Postgres Wiki 的查询时,我们会看到一大堆被阻塞的进程。解决它们的唯一方法是开始终止其中一些进程,直到 Postgres 能够再次解决问题。

现在我的理解是 Postgres 中的死锁检测器应该能够确定存在死锁,然后通过回滚一个被阻止的事务来继续解决它,以便另一个可以继续进行,然后重试第一个。 但这不是我们的情况,这些进程会无限期地陷入僵局,直到我们杀死它们。

deadlock_timeout 设置为 1s(默认值),我找不到用于打开或关闭死锁检测器的标志,因此我假设它始终处于打开状态。

所以我的问题是死锁检测器是否只能检测某些类型的死锁,如果可以,是哪些?

为什么 Lock Monitoring Wiki 上显示的查询可以检测到死锁进程,而死锁检测器本身却不能?

Now my understanding was that the deadlock detector in Postgres should be able to figure out that there is a deadlock and then proceed to resolve it by rolling back one of the blocked transactions, so that the other one could proceed and then retrying the first one.

只有当死锁发生在 PostgreSQL 后端之间时才会出现这种情况,其中每个后端都在等待另一个。

人们似乎常常期望死锁检测器也能处理死锁通过应用程序层的情况。事实并非如此。

死锁检测器可以处理这个:

  • tx1 锁定 table x 的第 1 行,尝试更新 table x
  • 的第 2 行
  • tx2 锁定 table x 的第 2 行,尝试更新 table x
  • 的第 1 行

不会帮助解决这个问题:

  • 拥有与 tx1 会话的应用程序线程 A 正在等待来自另一个线程 B
  • 的结果
  • tx1 在打开的事务中锁定 table x 的第 1 行
  • 应用程序线程 B 拥有与 tx2 的会话
  • tx2 正在尝试更新 table x 的第 1 行,tx1 已经持有

这两个都是死锁。 PostgreSQL 只能帮助第一个。它无法知道应用程序状态是什么,也不知道 tx1 永远无法继续并释放它对 tx2 正在等待的行的锁定,因为拥有它的应用程序线程正在等待 tx2 完成。死锁既涉及数据库中的等待,也涉及应用程序中的等待,两者都没有全貌。

请注意,一个查询等待另一个查询不是死锁。只有在存在无法打破的循环时才会出现死锁,因为如果没有另一个后端的进展,任何后端都无法进展,而且它们都在互相等待。

您的情况很可能与第二种情况类似,应用程序 陷入死锁。

Postgres 只有在看到两个事务相互等待时才会发现死锁。特别是对于两个(或更多)进程,场景必须是:

  • A需要获取被B锁定的资源
  • B需要获取被A锁定的资源

死锁处理不会处理以下情况:

  • A需要获取被B锁定的资源。
  • B 在交互式 psql 会话中锁定了 table 然后获取了 下午休息,不提交、回滚或注销。

根据您的描述,您的某个数据库会话似乎没有释放其资源。也许它缺少 COMMIT 等。就 Postgres 而言,这不是死锁,因为据它所知,锁被持有那么久是有充分正当理由的。

您可以做的一件事是设置锁定超时。这意味着进程在放弃并标记问题之前等待尝试获取锁的时间有一个上限。

锁定超时和其他设置记录在: http://www.postgresql.org/docs/9.3/static/runtime-config-client.html