[英]Deadlocks in PostgreSQL when running UPDATE
阅读有关 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
条件涵盖的整个 scope?
提前致谢!
在PostgreSQL中,行会在更新时被锁定 - 实际上,这实际上是有效的方式是每个元组(行的版本)都有一个名为xmin
的系统字段,用于指示哪个事务使该元组成为当前(通过插入或更新) )和一个名为xmax
的系统字段,用于指示哪个事务过期了该元组(通过更新或删除)。 当您访问数据时,它会检查每个元组,以确定您的事务是否可见,方法是根据这些值检查您的活动“快照”。
如果您正在执行UPDATE并且与您的搜索条件匹配的元组具有xmin,这将使您的快照和活动事务的xmax可见,它将阻塞,等待该事务完成。 如果首次更新元组的事务回滚,则事务会唤醒并处理该行; 如果第一个事务提交,则事务会唤醒并根据当前事务隔离级别执行操作。
显然,死锁是以不同顺序发生在行中的结果。 RAM中没有行级锁定,可以同时为所有行获取,但如果行以相同的顺序更新,则无法进行循环锁定。 不幸的是,建议的IN(1, 2)
语法并不能保证这一点。 不同的会话可能有不同的成本因素活动,后台“分析”任务可能会更改一个计划的生成与另一个计划之间的表的统计信息,或者它可能正在使用seqscan并受PostgreSQL优化的影响,导致新的seqscan加入一个已在进行中并“循环”以减少磁盘I / O.
如果您以相同的顺序,应用程序代码或使用游标一次执行一个更新,那么您将只有简单的阻塞,而不是死锁。 但是,一般而言,关系数据库容易出现序列化失败,最好通过一个框架访问它们,该框架将基于SQLSTATE识别它们并从一开始就自动重试整个事务。 在PostgreSQL中,序列化失败的SQLSTATE总是为40001或40P01。
http://www.postgresql.org/docs/current/interactive/mvcc-intro.html
PostgreSQL 是逐行锁定,还是锁定整个 scope
PostgreSQL 逐行锁定。
令人沮丧的是,没有办法像选择和插入那样对更新(或删除)进行排序。
解决方案是使用SELECT FOR UPDATE
和自加入。
UPDATE customer AS c SET ...
FROM (
SELECT ctid
FROM customer
WHERE id IN (1, 2)
ORDER BY id -- the optimal ordering varies, but it must be strict and consistent
FOR UPDATE
) AS c2
WHERE c.ctid = c2.ctid
(这里我使用行ctid
的物理 ID,这对于连接来说可以稍微快一些。)
PostgreSQL会查找记录,按顺序锁定记录,然后更新记录。
您可以检查查询计划以使自己相信这一点。
有一些开销,但它是最小的,特别是考虑到UPDATE
通常不是一个简单的操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.