我们有一个表没有对任何其他表的引用。
┬────────────┬─────────────┬───────────────┬───────────────╮
│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:
防止死锁的最佳方法通常是避免死亡 确定使用数据库的所有应用程序都获得锁定 多个对象按照一致的顺序。
但是我们怎样才能在上述场景中实现这一目标。是否有保证以特定顺序执行批量更新插入操作的方法?
答案 0 :(得分:17)
在所有竞争查询中的有序子查询中使用显式行级锁定。 (简单SELECT
不参与竞争。)
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;
这样,按照手册中的建议,行按照一致的顺序锁定。
假设id_A
,id_B
永远不会更新,即使是detailed in the "Caution" box in the manual等罕见的角落并发症,也是不可能的。
虽然不更新键列,但您可以使用较弱的lock mode FOR NO KEY UPDATE
。需要Postgres 9.3或更高版本。
另一个(慢且肯定)选项是使用Serializable Isolation Level进行竞争交易。您必须为序列化失败做好准备,在这种情况下您必须重试该命令。