【发布时间】:2017-11-14 16:19:21
【问题描述】:
我有一个通用函数可以操纵任何表的顺序(为什么与我的问题无关)。它读取当前值,计算出新值,设置它,然后返回它的计算,这就是插入的内容。这显然是一个多步骤的过程。
我从需要它的表上的 BEFORE INSERT 触发器调用它。
我只需要知道我是否保证在多用户环境中一次只能由一个调用者调用该函数?
具体来说,BEFORE INSERT 触发器是否必须在被另一个调用者再次调用之前完成?
从逻辑上讲,我会假设是的,但人们永远不知道幕后可能发生了什么。
如果答案是否定的,我需要对函数进行什么最小锁定以保证我可以以“线程安全”的方式读取和写入序列?
我正在使用 PG 10。
编辑
这里是用锁更新的函数:
CREATE OR REPLACE FUNCTION public.uts_set()
RETURNS TRIGGER AS
$$
DECLARE
sv int8;
seq text := format('%I.%I_uts_seq', tg_table_schema, tg_table_name);
BEGIN
EXECUTE format('LOCK TABLE %I IN ROW EXCLUSIVE MODE;', tg_table_name);
EXECUTE 'SELECT last_value+1 FROM ' || seq INTO sv; -- currval(seq) isn't useable
PERFORM setval(seq, GREATEST(sv, (EXTRACT(epoch FROM localtimestamp) * 1000000)::int8), false);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
但是,SELECT 已经获取了ROW EXCLUSIVE,因此该语句可能是多余的,可能需要更强的锁。或者,相反,它可能意味着不需要锁。
更新
如果我正确阅读了this SO question,那么我没有 LOCK 的原始版本应该可以工作,因为触发器获取了我更新后的函数冗余占用的相同锁。
【问题讨论】:
-
您确定这是
BEFORE INSERT触发器吗?您返回的是 NULL 而不是 NEW ......触发器定义也存在其他问题,但在此之前,为什么您需要序列的最后一个值和当前 epoch * 1000000 之间的最大数字? -
我不能 100% 确定
last_value会可靠地工作——即使没有并发也不能。 -
它与一个呼叫者完美配合。测试良好。
-
@michel.milezzi 感谢您纠正 NULL 错误。该函数返回当前时间戳,但通过使用序列保证它是唯一的。因此,例如,如果要插入一千条记录,它们将从当前时间戳开始,并以 1 为增量。
-
@IamIC 好的,为后代编辑了我的答案。问候!
标签: postgresql concurrency triggers locking