【问题标题】:PostgreSQL deadlock between select for update even ordering选择更新甚至排序之间的PostgreSQL死锁
【发布时间】:2014-01-14 04:29:23
【问题描述】:

我有两个死锁在一起的查询

PERFORM id
FROM stack
WHERE id IN (SELECT tmp.stkid FROM tmp_push_bulk tmp WHERE tmp.stkid > 0)
ORDER BY id
FOR UPDATE OF stack

PERFORM stk.id
FROM stack stk
WHERE stk.referer IN (
      SELECT tmp.id
      FROM tmp_renew_stk tmp
)
ORDER BY stk.id
FOR UPDATE OF stk

错误是:

- PG (20:46:37) [14786]: Execute command failed: ERROR:  deadlock detected
DETAIL:  Process 14797 waits for ShareLock on transaction 183495696; blocked by process 24303.
Process 24303 waits for ShareLock on transaction 183495704; blocked by process 14797.
HINT:  See server log for query details.

我也认为每个进程都按照 id 列的顺序锁定它的行,所以死锁是不可能的。谁能告诉我为什么?

【问题讨论】:

    标签: postgresql concurrency postgresql-9.2 database-deadlocks


    【解决方案1】:

    可能是IN 表达式以非特定顺序锁定行(未经测试)。我通常会在可能的情况下用JOIN 替换更大的集合上的IN。这开始时速度更快,从而最大限度地减少了死锁的机会。我会尝试:

    更新:根据您的评论,您有很多重复项。假设临时表中存在欺骗,我建议出于三个目的使用子查询:

    • 折叠副本。
    • 使用DISTINCT 在子查询中同时对行进行排序特别便宜。
    • 申请WHERE条件AND tmp.stkid > 0

    PERFORM s.id
    FROM  stack s
    JOIN  (
       SELECT DISTINCT stkid
       FROM   tmp_push_bulk
       WHERE  stkid > 0
       ) tmp ON tmp.stkid = s.id 
    ORDER BY 1
    FOR   UPDATE OF s;
    

    还有:

    PERFORM s.id
    FROM  stack s
    JOIN  (
       SELECT DISTINCT id
       FROM   tmp_renew_stk
       ) tmp ON tmp.id = s.referer
    ORDER BY 1
    FOR   UPDATE OF s;
    

    【讨论】:

    • 嗯,因为有很多重复的行,所以我使用IN条件自动删除它。来自 PostgreSQL 文档 (postgresql.org/docs/9.0/static/…):使用 ORDER BY 和 FOR UPDATE/SHARE 的 SELECT 命令可能会乱序返回行。这是因为先应用 ORDER BY。
    • @TruongHua: 是临时表还是stack 表中的重复项?您对ORDER BY 的发现可能与您的死锁有关,如果两个实例尝试以相同的顺序锁定行,但其中一个实例在另一个并发事务中的 UPDATE 后看到某些行的不同值因此订购方式不同。不过,这是一个不太可能发生的情况。如果您在表stack 中有重复项,则更可能的解释是:重复项的顺序没有明确定义!您可以通过向 ORDER BY 添加更多表达式来解决此问题,以使其明确。
    • 仅在 tmp 表中重复
    猜你喜欢
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多