运行UPDATE时PostgreSQL中的死锁

时间:2012-04-20 11:41:48

标签: postgresql transactions database-deadlocks

关于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条件涵盖的整个范围?

提前致谢!

1 个答案:

答案 0 :(得分:37)

在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