在执行批量更新和删除操作时避免 PostgreSQL 死锁 [英] Avoiding PostgreSQL deadlocks when performing bulk update and delete operations

查看:98
本文介绍了在执行批量更新和删除操作时避免 PostgreSQL 死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We have a single table which does not have references to any other tables.

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

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

The primary key of the table is a composite of id_A and id_B.

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

Reads and writes of this table are highly concurrent and the table has millions of rows. We have several stored procedures which do mass updates and deletes. Those stored procedures are being called concurrently mainly by triggers and application code.

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

The operations usually look like the following where it could match thousands of records to update/delete:

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 的行级和表级锁)执行操作似乎都无法解决这些死锁问题.(请注意,由于性能影响,我们不能以任何方式对该表使用访问排他锁)

We are experiencing deadlocks and all our attempts to perform operations with locks (row level using SELECT FOR UPDATE and table level locks) do not seem to solve these deadlock issues. (Note that we cannot in any way use access exclusive locking on this table because of the performance impact)

还有其他方法可以尝试解决这些死锁情况吗?参考手册说:

Is there another way that we could try to solve these deadlock situations? The reference manual says:

防止僵局的最佳防御通常是通过以下方式避免它们确定所有使用数据库的应用程序都获取锁定多个对象以一致的顺序排列.

The best defense against deadlocks is generally to avoid them by being certain that all applications using a database acquire locks on multiple objects in a consistent order.

但是我们如何在上述场景中实现这一点.是否有保证按特定顺序进行批量更新插入操作的方法?

But how could we achieve this in the above scenario. Is there a guaranteed way to do bulk update inset operations in a particular order?

推荐答案

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

Use explicit row-level locking in ordered subqueries in all competing queries.
(SELECT does not compete with write-locks.)

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 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;

这样,行就会按照手册中的建议以一致的顺序锁定.

This way, rows are locked in consistent order as advised in the manual.

假设 id_Aid_B 永远不会更新,即使是像 详见注意事项"手册中的框是不可能的.

Assuming that id_A, id_B are never updated, even rare corner case complications like detailed in the "Caution" box in the manual are not possible.

虽然不更新键列,但您可以使用较弱的 锁定模式FOR NO KEY UPDATE.需要 Postgres 9.3 或更高版本.

While not updating key columns, you can use the weaker lock mode FOR NO KEY UPDATE. Requires Postgres 9.3 or later.

另一个(并且肯定)选项是使用 用于竞争事务的可序列化隔离级别.您必须为序列化失败做好准备,在这种情况下,您必须重试该命令.

The other (slow and sure) option is to use the Serializable Isolation Level for competing transactions. You would have to prepare for serialization failures, in which case you have to retry the command.

这篇关于在执行批量更新和删除操作时避免 PostgreSQL 死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆