PostgreSQL中的deadlock_timeout配置参数检测哪种类型的死锁?

我的总体目标是在检测到死锁时从Postgres获取40P01/ERRCODE_T_R_DEADLOCK_DETECTED错误代码。(更准确地说,我希望在Hibernate中获得LockAcquisitionException异常,它是40P01/ERRCODE_T_R_DEADLOCK_DETECTED错误代码在PostgreSQL81Dialect.java中的翻译)我使用的是PostgreSQL 9.6。

为此,我认为应该设置deadlock_timeoutlog_lock_waits配置变量,如19.12. Lock Management所建议。

deadlock_timeout(整数)这是在检查死锁条件之前等待锁的时间,以毫秒为单位。 [...] 当设置了log_lock_waits时,该参数还确定等待锁的时间长度,然后发出有关锁等待的日志消息。如果您想要调查锁定延迟,可以将deadlock_timeout设置得比正常时间短。

我在postgresql.conf中设置了以下值。

log_lock_waits = on         # log lock waits >= deadlock_timeout
deadlock_timeout = 5s
现在,当我使用Hibernate从Java创建死锁情况时,我会在postgresql.log中找到以下内容。
LOG:  process 17493 still waiting for ShareLock on transaction 322815 after 5000.464 ms
DETAIL:  Process holding the lock: 17495. Wait queue: 17493.
CONTEXT:  while updating tuple (132,35) in relation "publication"
然而,没有生成40P01/ERRCODE_T_R_DEADLOCK_DETECTED错误(也没有发送给JDBC驱动程序)。 我稍微研究了一下Postgres源代码,并发现通过设置deadlock_timeout/log_lock_waits进行的死锁检测是与生成40P01/ERRCODE_T_R_DEADLOCK_DETECTED的机制不同的。处理deadlock_timeout情况的代码位于backend/storage/lmgr/proc.c中,而处理40P01/ERRCODE_T_R_DEADLOCK_DETECTED情况的代码位于backend/storage/lmgr/deadlock.c中。 所以,我的问题是: 这实际上是两种不同类型的死锁吗? 当基于deadlock_timeout的死锁检测发生时,有没有办法得到一个错误? 如何强制发生ERRCODE_T_R_DEADLOCK_DETECTED错误? 更新:我用来陷入死锁情况的代码如下(Spring/Java):
// This is in a Transaction managed by spring
Publication p = em.find(Publication.class, id);
p.setComment("Outer "+new Date());
em.flush();

// This utility method runs the lambda expression in a new Transaction
// using Spring's TransactionTemplate and tries to update
// the same Publication that is about to be updated by the 
// "outer" transaction
Utils.runInSeparateTransaction(status -> {
    Publication p2 = em.find(p.getClass(), p.getMtid());
    p2.setComment("Inner "+new Date());
    return p2;
    // Would flush/commit after the return, but will "hang" instead.
    // Here I would expect the deadlock error but only get the
    // deadlock log.
};

// Outer transaction would commit at this point but will 

你是如何制造死锁的情况?Postgres只能检测到在Postgres内部发生的死锁。 - jjanes
嗨,我添加了一段代码片段,用于获取当前的死锁情况,导致 进程 17493 在经过 5000.464 毫秒后仍在等待事务 322815 的 ShareLock - Balázs E. Pataki
1个回答

死锁是多个事务在它们交叉获取的锁上发生冲突的情况。这种情况无法解决,除非中止其中一个事务。引擎会将此类事务中止,并将错误代码设置为ERRCODE_T_R_DEADLOCK_DETECTED。 log_lock_waits与死锁没有直接关系,它的作用是提醒一些查询因等待锁而被阻塞,意味着其他事务持有锁的时间过长。 这实际上并不是两种不同类型的死锁,你可能对锁和死锁产生了混淆。 当基于deadlock_timeout的死锁检测发生时,是否有办法获得错误信息? 这是自动的。如果没有出现该错误,那是因为没有发生死锁。 如何强制发生ERRCODE_T_R_DEADLOCK_DETECTED错误? 通过创建至少两个相互冲突的事务来实际产生死锁。在S.O.上查看postgres deadlock without explicit locking以获取一个简单示例。

对于"log_lock_waits必须小于deadlock_timeout",log_lck_waits是一个布尔值。 - CodeFarmer
@CodeFarmer: 眼力真好,谢谢。我已经删除了那段落,回想起来,它显然是错误的。 - Daniel Vérité