可以选择... NOWAIT“死锁”吗?

时间:2019-08-20 09:29:29

标签: postgresql

医生说(重点是我):

  

FOR UPDATE,FOR NO KEY UPDATE,FOR SHARE和FOR KEY SHARE是锁定子句;它们会影响SELECT如何锁定从表中获取的行

     

如果无法立即锁定所选行,则使用NOWAIT,该语句报告错误,而不是等待。

因此,我不确定是否可能发生以下行为。

想象一下,从并发连接S1和S2运行同一条SELECT ... NOWAIT语句,该语句返回R1和R2行。

  1. S1获取并锁定R1。
  2. S2获取并锁定R2。
  3. S1尝试获取R2,但它已被S2锁定。
  4. S2尝试获取R1,但它已被S1锁定。
  5. 由于S1失败,释放了对R1的锁定。

问题是步骤4是否实际上可以发生在步骤3和5之间,还是相对于并发选择自动执行步骤3和5。

我猜测它不会发生,因为如果没有NOWAIT(或SKIP LOCKED),此行为将导致死锁(S1等待S2完成并释放R2,而S2等待S1完成并发布R1),但也许可以通过其他方式解决这种情况。

那么,这里有哪些保证?

1 个答案:

答案 0 :(得分:3)

在您的方案中,如果第3步试图使用NOWAIT选项获得R2,则该步骤应该失败。如果失败,则立即释放S1的所有锁,并且必须回滚S1。因此,S2可以在步骤4中获得R1。这种情况不会死锁。

如果您尝试在步骤3 中没有 NOWAIT选项中获得R2,则步骤4将由于检测到死锁而失败,并且S2将立即释放其所有锁。

保证是关系数据库中锁的定义行为,如documentation中所述:

  

要防止操作等待其他事务提交,请使用NOWAIT选项。使用NOWAIT时,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。

报告错误表示事务已中断,必须回滚。事务是原子的。简而言之,这意味着只有成功完成了它包含的所有步骤后,才能终止提交。