【发布时间】:2019-07-11 22:02:14
【问题描述】:
我们有一个带有简单表格的应用程序
given_entity{
UUID id;
TimeStamp due_time;
TimeStamp process_time;
}
这是一个 Spring Boot (1.2.5.RELEASE) 应用程序,它使用 spring-data-jpa.1.2.5.RELEASE 和 hibernate-4.3.10.FINAL 作为 jpa 提供者。
我们有这个应用程序的 5 个实例,每个实例都有一个调度程序,每 2 秒运行一次,并在数据库中查询截止时间为最后 2 分钟但尚未处理的行;
SELECT * FROM given_entity
WHERE process_time is null and due_time between now() and NOW() - INTERVAL '2 minutes'
FOR UPDATE
要求是上表的每一行都被恰好一个应用程序实例成功处理。 然后应用程序实例处理这些行并在一个事务中更新其 process_time 字段。 这可能需要也可能不会超过 2 秒,这是调度程序间隔。 此外,我们在此表上没有任何索引,但 PK 索引。 值得注意的第二点是,这些实例可能会在该表中插入由客户端单独调用的行。
问题:在日志中我看到来自 postgresql 的这条消息(很少但会发生)
ERROR: deadlock detected
Detail: Process 10625 waits for ShareLock on transaction 25382449; blocked by process 10012.
Process 10012 waits for ShareLock on transaction 25382448; blocked by process 12238.
Process 12238 waits for AccessExclusiveLock on tuple (1371,45) of relation 19118 of database 19113; blocked by process 10625.
Hint: See server log for query details.
Where: while locking tuple (1371,45) in relation "given_entity"
问题: 这是怎么发生的? 我检查了 postgresql 锁并搜索了互联网。我没有发现任何说死锁只能在一张简单的桌子上发生的事情。 我也无法使用测试重现此错误。
【问题讨论】:
-
你能把代码粘贴到你更新表格的地方吗? select for update 阻止其他事务的修改,应该小心。
-
要完全避免这些问题,您可以使用 Hibernate 乐观锁定机制(除非有一些真正正当的理由来锁定您想要更新的记录)。也许这也可能有帮助:blog.2ndquadrant.com/…
-
对于更新,我们只需更新其中的 process_time 字段。类似于 'UPDATE given_entity set process_time= now() where id = ?'
-
对于乐观锁,我们不能使用它,因为要求是每一行都必须由一个实例完全处理。使用乐观锁,两个实例可以同时处理该行,当第二个实例得到 OptimisticLockException 时,为时已晚,该行已经被处理。我们希望保持简单,并使用 DB 行锁来同步我们应用程序的不同实例。
标签: postgresql hibernate deadlock