同一张表上的两个“SELECT FOR UPDATE”语句会导致死锁吗?

Posted

技术标签:

【中文标题】同一张表上的两个“SELECT FOR UPDATE”语句会导致死锁吗?【英文标题】:Can two "SELECT FOR UPDATE" statements on the same table cause a deadlock? 【发布时间】:2012-10-10 18:10:48 【问题描述】:

假设两个同时事务在 Postgresql DB 上执行以下查询:

交易 A:

SELECT * FROM mytable WHERE id IN (1, 2, 3, 4) FOR UPDATE

事务 B:

SELECT * FROM mytable WHERE id IN (6, 3, 2, 1) FOR UPDATE

是否可能由于 Postgresql 以不一致的顺序获取行锁而导致死锁?例如。如果 Postgresql 按照本例中给出的 id 顺序获取行锁,则可能会出现死锁。

或者 Postgresql 内部是否足够智能以始终以同一表上的同时、离散的 SELECT FOR UPDATE 语句不会相互死锁的方式获取行锁(例如,始终按主键的顺序获取行锁)?

如果 Postgresql 自动防止这种死锁的发生,有没有办法修改查询以防止这种情况发生(例如,如果事实上 Postgresql 按 id 的顺序获取行锁给定,那么一致地对 id 进行排序应该可以防止死锁)?

感谢您的帮助!

【问题讨论】:

这个问题符合dba.SE的要求。 IN( .. , ..) 集合并不意味着 有序 集合。它只是一个集合(项目的集合),类似于select ... 的结果,它是一个(无序的)元组集合。换句话说:评估/执行的顺序是undefined 这不是您问题的真正答案,但是,如果您害怕死锁,为什么不提升隔离级别SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 这样,第二个 SELECT 将被拒绝。 【参考方案1】:

对不起,我有另一个答案,但它是错误的。

文档声明在 FOR UPDATE 子句之前应用了 ORDER BY 子句。因此,无论选择行的顺序如何,都可以获取锁(我已经通过测试确认了这一点)。如果您需要以不同的顺序选择它们,您可以使用:

SELECT * FROM (SELECT * FROM table ORDER BY id FOR UPDATE) ORDER BY another_column;

您可能想在 PostgreSQL mailing list 上尝试您的问题。

【讨论】:

您有相关文档页面的链接吗?你说的有道理,但我没有看到 postgresql.org/docs/9.1/static/explicit-locking.html 上提到的 ORDER BY。 在 SELECT/FOR UPDATE 部分:postgresql.org/docs/9.1/static/… 感谢您的回答。我确实在文档中看到了这一点,但对我来说,并不清楚实际的行锁定是按照ORDER BY 子句确定的顺序对行进行的。该描述可以理解为ORDER BY 只是预先确定了输出顺序(一旦获得所有锁,可能不再准确),但对实际的锁定顺序没有影响。【参考方案2】:

来自http://www.postgresql.org/docs/9.1/static/explicit-locking.html:

PostgreSQL 自动检测死锁情况并通过中止涉及的事务之一来解决它们

该页面使用了一个涉及UPDATEs 的示例,就锁定而言,这相当于SELECT ... FOR UPDATE

【讨论】:

Postgres 可以解决死锁,是的。但这并不能回答问题,即在此设置中是否会发生死锁。 @ErwinBrandstetter:我希望它可以,因为SELECT 返回的行的顺序是未指定的。 ORDER BY 子句应该可以解决这个问题。

以上是关于同一张表上的两个“SELECT FOR UPDATE”语句会导致死锁吗?的主要内容,如果未能解决你的问题,请参考以下文章

Mysql在同一张表上的多个左连接

同一张表上的多个连接,在一个查询中计数

与关联对象在同一张表上的多对多关系

同一张表上的多个事务,同时

更新查询在同一张表上的 Sql 查询死锁

同一张表上的多个连接:转换状态