【发布时间】:2023-04-07 06:02:01
【问题描述】:
我在 postgresql 中有两个表:
table A: has the definitions for a certain object
table B: has the instances of the objects defined in table A
表 A 有列 total_instances 和 per_player_instances,它们都可以为空,如果设置了它们,我需要防止表 B 中的实例计数超过。该代码处理大多数情况,但我从并发插入中得到重复。
不需要锁定表 A,因为它很少更改,如果我们这样做,我们可以在表 B 中不会发生插入的计划停机时间内进行。
我编写了一个触发器来计算现有实例并在计数超过时返回错误,但我一定是锁定错误,因为现在我遇到了 deadlock_detected 错误。我的触发函数是这样的:
CREATE OR REPLACE FUNCTION ri_constraints_func() RETURNS trigger AS $$
DECLARE
max_total INTEGER := NULL;
max_per_player INTEGER := NULL;
total_count INTEGER := 0;
per_player_count INTEGER := 0;
BEGIN
-- prevent concurrent inserts from multiple transactions
SELECT INTO max_total, max_per_player
awardable_total, awardable_per_player
FROM t1
WHERE id = NEW.t1_id;
LOCK TABLE t2 IN EXCLUSIVE MODE;
IF max_total IS NOT NULL THEN
SELECT INTO total_count
count(1)
FROM t2
WHERE t1_id = NEW.t1_id;
IF total_count >= max_total THEN
RAISE EXCEPTION 'awardable_total_exceeded';
END IF;
END IF;
IF max_per_player IS NOT NULL THEN
SELECT INTO per_player_count
count(1)
FROM t2
WHERE t1_id = NEW.t1_id
AND awarded_to_player_id = NEW.awarded_to_player_id;
IF per_player_count >= max_per_player THEN
RAISE EXCEPTION 'awardable_per_player_exceeded';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
基本上我需要做的是防止在计算实例和执行插入之间插入表。我认为使用LOCK TABLE t2 IN EXCLUSIVE MODE; 可以做到这一点。我正在对表锁定进行更多研究,但如果有人知道使用什么锁定级别来完成此操作,我将不胜感激。
另外,我不喜欢这种特殊的方法,所以如果要让它工作需要重新编写这个函数,我也愿意。
Postgresql 版本是 11。
【问题讨论】:
-
没有必要明确锁定任何东西。
标签: database postgresql database-deadlocks database-locking