postgresql - Postgres 9.4 在单个表上读取-修改-写入时检测到死锁

标签 postgresql hibernate deadlock

我们有一个带有简单表格的应用程序

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 provier。
我们有这个应用程序的 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 锁并搜索了互联网。我没有发现任何说死锁只可能在一张简单的表上发生的事情。 我也无法使用测试重现此错误。

最佳答案

进程 A 尝试锁定第 1 行,然后锁定第 2 行。同时,进程 B 尝试锁定第 2 行,然后锁定第 1 行。这就是触发死锁所需的全部。

问题是行锁的获取顺序不确定,因为 SELECT 以不确定的顺序返回其行。避免这种情况只是确保所有进程在锁定行时就顺序达成一致的问题,即:

SELECT * FROM given_entity 
WHERE process_time is null and due_time between  now() and NOW() - INTERVAL '2 minutes' 
ORDER BY id
FOR UPDATE

在 Postgres 9.5+ 中,您可以使用 FOR UPDATE SKIP LOCKED 简单地忽略被另一个进程锁定的任何行。

关于postgresql - Postgres 9.4 在单个表上读取-修改-写入时检测到死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40083578/

相关文章:

sql - 在全局表中存储 WHERE 子句

ruby-on-rails - 在 Rails 中考虑订单的股票建模

hibernate - 无法删除或更新父行 : a foreign key constraint fails with Join tables

java - Criteria.list() 期间出现 org.hibernate.TransientObjectException

c++ - 如何防止用餐哲学家c++中的死锁

postgresql - WAL 缓冲区在事务提交之前填满

hibernate - 当我有@ManyToMany 时如何设置外键名称

mysql - 为什么我使用 SELECT ... FOR UPDATE 锁从 mysql 中获得死锁?

MySQL 亚马逊 RDS : Lock Wait timeout exceeded

postgresql - PostgreSQL 上 $libdir 的问题