阅读有关Postgresql死锁的一点点困惑.
一个典型的僵局的例子是:
-- Transaction 1 UPDATE customer SET ... WHERE id = 1 UPDATE customer SET ... WHERE id = 2 -- Transaction 2 UPDATE customer SET ... WHERE id = 2 UPDATE customer SET ... WHERE id = 1
但是如果我更改代码如下:
-- Transaction 1 UPDATE customer SET ... WHERE id IN (1,2) -- Transaction 2 UPDATE customer SET ... WHERE id IN (1,2)
这将是一个僵局的可能吗?
本质上我的问题是:在第二种情况下,Postgresql会一行一个地锁定一行,还是锁定WHERE条件覆盖的整个范围?
提前致谢!
在Postgresql中,这些行将被更新时被锁定 – 实际上,这个实际工作的方式是每个元组(一行的版本)都有一个名为xmin的系统字段来指示哪个事务使该元组当前(通过插入或更新)和一个名为xmax的系统字段来指示哪个事务已过期该元组(通过更新或删除).当您访问数据时,它会检查每个元组,以确定它是否对您的事务可见,通过根据这些值检查您的活动“快照”.
如果您正在执行UPDATE,并且与您的搜索条件匹配的元组具有xmin,这将使您的快照和活动事务的xmax可见,它将阻止等待该事务完成.如果首次更新元组的事务回滚,则您的事务将唤醒并处理该行;如果第一个事务提交,您的事务将唤醒并根据当前事务隔离级别执行操作.
显然,这种情况的发生是以不同顺序排列的.在RAM中没有可以同时获取所有行的行级锁定,但是如果行以相同的顺序更新,则不能进行循环锁定.不幸的是,建议的IN(1,2)语法不能保证.不同的会话可能具有不同的成本因素的活动,背景“分析”任务可能会改变在生成一个计划和另一个计划之间的表的统计信息,或者可能使用seqscan并受到Postgresql优化的影响,这导致新的排序加入一个已经进行中的“循环”来减少磁盘I / O.
如果您以同样的顺序,应用程序代码或使用游标进行一次更新,那么您将只有简单的阻止,而不是死锁.一般而言,关系型数据库容易出现序列化失败,最好通过框架访问它们,该框架将基于sqlSTATE来识别它们,并从一开始就自动重试整个事务.在Postgresql中,序列化失败将始终具有40001或40P01的sqlSTATE.
http://www.postgresql.org/docs/current/interactive/mvcc-intro.html