【问题标题】:Deadlock when using SELECT FOR UPDATE使用 SELECT FOR UPDATE 时出现死锁
【发布时间】:2019-06-13 06:34:01
【问题描述】:

我注意到并发执行简单且相同的查询类似于

BEGIN;  
SELECT files.data FROM files WHERE files.file_id = 123 LIMIT 1 FOR UPDATE;
UPDATE files SET ... WHERE files.file_id = 123;
COMMIT;

导致死锁,这让我感到惊讶,因为这样的查询看起来不应该造成死锁。另外:完成这样的请求通常只需要几毫秒。在这种死锁情况下,如果我运行:

 SELECT blockeda.pid AS blocked_pid, blockeda.query as blocked_query, 
 blockinga.pid AS blocking_pid, blockinga.query as blocking_query FROM pg_catalog.pg_locks blockedl
 JOIN pg_stat_activity blockeda ON blockedl.pid = blockeda.pid
 JOIN pg_catalog.pg_locks blockingl     ON(blockingl.transactionid=blockedl.transactionid
 AND blockedl.pid != blockingl.pid)
 JOIN pg_stat_activity blockinga ON blockingl.pid = blockinga.pid
 WHERE NOT blockedl.granted;

在整个死锁期间,我看到为 blocked_pid 和 blockin_pid 列出了两个相同的选择语句。

所以我的问题是:尝试选择同一行FOR UPDATE 导致死锁的查询是否正常和预期?如果是这样,在这种情况下避免死锁的最佳策略是什么?

【问题讨论】:

  • 可以发CREATE TABLE files吗?
  • 这样的问题需要准确的表定义和 Postgres 版本。

标签: sql postgresql concurrency sql-update deadlock


【解决方案1】:

你的命令是矛盾的。

如果files.file_id 定义为UNIQUE(或PRIMARY KEY),则不需要LIMIT 1。而且您根本不需要显式锁定。只需运行UPDATE,因为整个事务中只有一行受到影响,所以不会出现死锁。 (除非触发器或规则或涉及的函数有副作用。)

如果files.file_id 不是UNIQUE(看起来如此),那么UPDATE 可以以任意顺序影响多行,并且只有其中一个被锁定,这是死锁的秘诀。那么更直接的问题是查询没有执行您似乎想要开始的操作。

最佳解决方案取决于缺失的信息。这会起作用:

UPDATE files
SET    ...
WHERE  primary_key_column = (
         SELECT primary_key_column
         FROM   files 
         WHERE  file_id = 123
         LIMIT  1
     --  FOR    UPDATE SKIP LOCKED
         );

单个命令不需要BEGIN; 和COMMIT;,而默认的自动提交是启用的。

如果行已被锁定,您可能需要添加 FOR UPDATE SKIP LOCKED (or FOR UPDATE NOWAIT) 以跳过或报告错误。

您可能希望添加一个WHERE 子句以避免重复处理同一行。

更多:

【讨论】:

  • 感谢您回答@Erwin Brandstetter! files 表确实有多个UNIQUE 约束,SELECT 使用它们来唯一标识单行。但是UPDATE 语句不会更改任何具有唯一约束的字段,因此我认为这应该使整个操作原子化。
  • 另外:感谢您提供有关使用UPDATE 和WHERE 的建议,但在我的情况下,更新逻辑要复杂得多,并且涉及任意函数调用来计算新文件数据,所以我不能将它们放入单个 SQL 语句中。我将尝试重构我的代码以使用 files 主键,看看是否能解决问题。
  • 嗯...我重构了我的代码以使用主键来选择行(并按照您的建议删除LIMIT 1),但这并没有解决问题:我仍然遇到死锁。知道这里还有什么可以玩的吗?谢谢,
  • @Yatima: my case ... involved arbitrary functions calls to calculate new file-data 那里也可能发生死锁。在不了解完整情况的情况下很难猜测。
  • 我能够做到这一点并创建更简单的测试用例。为清楚起见,我将其移至单独的问题中,请参阅:stackoverflow.com/questions/54317754/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 2013-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多