【问题标题】:Avoiding PostgreSQL deadlocks when performing bulk update and delete operations执行批量更新和删除操作时避免 PostgreSQL 死锁
【发布时间】:2015-01-16 09:38:27
【问题描述】:

我们有一个没有对任何其他表的引用的表。

┬────────────┬─────────────┬───────────────┬───────────────╮
│id_A(bigint)│id_B(bigint) │val_1(varchar) │val_2(varchar) │
╪════════════╪═════════════╪═══════════════╪═══════════════╡

表的主键是 id_A 和 id_B 的组合。

该表的读写是高度并发的,表有数百万行。 我们有几个存储过程可以进行大量更新和删除。这些存储过程主要由触发器和应用程序代码同时调用。

操作通常如下所示,它可以匹配数千条记录以更新/删除:

DELETE FROM table_name 
WHERE id_A = ANY(array_of_id_A)
AND id_B = ANY(array_of_id_B)

UPDATE table_name
SET val_1 = 'some value', val_2 = 'some value'
WHERE id_A = ANY(array_of_id_A)
AND id_B = ANY(array_of_id_B)

我们遇到了死锁,我们所有使用锁(使用SELECT FOR UPDATE 的行级和表级锁)执行操作的尝试似乎都不能解决这些死锁问题。 (请注意,由于性能影响,我们不能以任何方式对该表使用访问排他锁定)

还有其他方法可以尝试解决这些死锁情况吗? The reference manual says:

防止死锁的最佳方法通常是通过以下方式避免死锁 确保所有使用数据库的应用程序都获得锁定 多个对象以一致的顺序。

但是在上述情况下我们如何实现这一点。是否有保证以特定顺序进行批量更新插入操作的方法?

【问题讨论】:

    标签: database postgresql concurrency deadlock database-deadlocks


    【解决方案1】:

    在所有竞争查询中的有序子查询中使用显式行级锁定
    SELECT 不与写锁竞争。)

    DELETE

    DELETE FROM table_name t
    USING (
       SELECT id_A, id_B
       FROM   table_name 
       WHERE  id_A = ANY(array_of_id_A)
       AND    id_B = ANY(array_of_id_B)
       ORDER  BY id_A, id_B
       FOR    UPDATE
       ) del
    WHERE  t.id_A = del.id_A
    AND    t.id_B = del.id_B;
    

    UPDATE

    UPDATE table_name t
    SET    val_1 = 'some value'
         , val_2 = 'some value'
    FROM  (
       SELECT id_A, id_B
       FROM   table_name 
       WHERE  id_A = ANY(array_of_id_A)
       AND    id_B = ANY(array_of_id_B)
       ORDER  BY id_A, id_B
       FOR    NO KEY UPDATE  -- Postgres 9.3+
    -- FOR    UPDATE         -- for older versions or updates on key columns
       ) upd
    WHERE  t.id_A = upd.id_A
    AND    t.id_B = upd.id_B;
    

    这样,行将按照手册中的建议以一致的顺序锁定。

    假设id_Aid_B 永远不会更新,那么即使像detailed in the "Caution" box in the manual 这样的罕见极端情况也是不可能的。

    虽然不更新关键列,但您可以使用较弱的lock mode FOR NO KEY UPDATE。需要 Postgres 9.3 或更高版本。


    另一个(并且肯定)选项是使用Serializable Isolation Level 进行竞争交易。您必须为序列化失败做好准备,在这种情况下,您必须重试该命令。

    【讨论】:

    • 感谢@Erwin 的回复。我已经使用了您的策略,但仍然陷入僵局。我看不出这种方法有什么明显的问题。我希望这些行按顺序锁定。任何更多的尝试建议将不胜感激。
    • @sanjayav:您是否分析了日志以查看实际涉及哪些关系和查询?也许您还有其他忘记适应的查询?是否存在涉及乱序访问表的触发器?
    • 由于我的团队一直面临的僵局,我一直在抓狂,这就是票,感谢@ErwinBrandstetter!
    猜你喜欢
    • 2017-10-18
    • 2020-10-25
    • 1970-01-01
    • 1970-01-01
    • 2020-01-11
    • 1970-01-01
    • 2015-08-18
    • 2017-05-04
    • 2018-07-12
    相关资源
    最近更新 更多