我的总体目标是在检测到死锁时从Postgres获取40P01/ERRCODE_T_R_DEADLOCK_DETECTED错误代码。(更准确地说,我希望在Hibernate中获得LockAcquisitionException异常,它是40P01/ERRCODE_T_R_DEADLOCK_DETECTED错误代码在PostgreSQL81Dialect.java中的翻译)我使用的是PostgreSQL 9.6。
为此,我认为应该设置deadlock_timeout和log_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
进程 17493 在经过 5000.464 毫秒后仍在等待事务 322815 的 ShareLock。 - Balázs E. Pataki