PostgreSQL:死锁在事务中检测到SELECT FOR UPDATE

时间:2018-06-27 20:20:47

标签: postgresql transactions sql-update

我有以下架构

  

ID(PK)| REF_ID |活动|状态

ID-主键

我正在使用以下查询来选择和更新

BEGIN;    
select * from table where ref_id = $1 and is_active is true for update;
UPDATE table set status = $1 where id =$2;
END;
上面的

说明

1)选择查询结果将用于锁定具有提供的引用ID的所有行,并将该结果用于某些业务逻辑

2)更新查询以更新属于相同引用ID的行的状态

问题

postgres@machine ERROR:  deadlock detected
postgres@machine DETAIL:  Process 28297 waits for ShareLock on transaction 4809510; blocked by process 28296.
        Process 28296 waits for ShareLock on transaction 4809502; blocked by process 28297.
        Process 28297: select * from jobs where ref_id ='a840a8bd-b8a7-45b2-a474-47e2f68e702d' and is_active is true for update
        Process 28296: select * from jobs where ref_id ='a840a8bd-b8a7-45b2-a474-47e2f68e702d' and is_active is true for update 
postgres@machine ERROR:  deadlock detected
postgres@machine DETAIL:  Process 28454 waits for ShareLock on transaction 4810111; blocked by process 28384.
        Process 28384 waits for ShareLock on transaction 4810092; blocked by process 28297.
        Process 28297 waits for AccessExclusiveLock on tuple (113628,5) of relation 16817 of database 16384; blocked by process 28454.
        Process 28454: select * from jobs where ref_id ='a840a8bd-b8a7-45b2-a474-47e2f68e702d' and is_active is true for update
        Process 28384: select * from jobs where ref_id ='a840a8bd-b8a7-45b2-a474-47e2f68e702d' and is_active is true for update
        Process 28297: select * from jobs where ref_id ='a840a8bd-b8a7-45b2-a474-47e2f68e702d' and is_active is true for update

此表用于高度并发和分布式应用程序(100并行使用相同的ref_id),这就是为什么我想通过选择然后在同一事务中进行更新来避免分布式锁定。但是我面临着这个死锁错误,我不知道不知道为什么显式锁定不起作用。

预期的行为是,如果具有相同参考ID的其他任何人获得了锁,则具有相同参考ID的任何其他作业都必须等待

帮助,我可以找出丢失的内容或其他解决方法。即使在显式锁定并处于事务内之后,仍然不清楚为什么会发生死锁。

1 个答案:

答案 0 :(得分:2)

如Laurenz所说,在这种简单情况下,您应该能够消除在锁定查询中使用ORDER BY造成死锁的可能性。

例如在以下情况下会出现死锁:

  • 进程A在第1行获得锁
  • 进程B在第2行获取了一个锁
  • 进程A请求对第2行进行锁定(并等待B释放它)
  • 进程B请求对第1行进行锁定(并等待A释放它)

...并且在这一点上,进程将永远彼此等待(或者直到服务器注意到并杀死其中一个)。

但是,如果两个进程都提前同意锁定第1行然后锁定第2行,则不会发生这种情况。一个过程仍在等待另一个,但是另一个可以自由进行。

更一般地说,只要所有进程都同意在获取锁时遵循相同的顺序,就可以确保至少有一个始终在取得进展;如果您只尝试获取比已经拥有的锁“高”的锁,那么拥有“最高”锁的人将永远不会等待任何人。

排序必须明确且随时间稳定,因此生成主键是理想的(即,您应该ORDER BY id)。

相关问题