触发器有时会因重复的键错误而失败

时间:2019-04-22 21:28:07

标签: postgresql triggers race-condition

我正在AWS中使用PostgreSQL RDS实例。基本上,有一个将数据插入第一个表的查询,我们称其为table。那里的数据在某些字段中可能有重复项(显然,主键除外)。

然后有一个触发器来更新另一个表infotable,不允许重复。

触发器:

CREATE TRIGGER insert_infotable AFTER INSERT ON table
    FOR EACH ROW EXECUTE PROCEDURE insert_infotable();

触发函数的相关部分如下所示:

CREATE OR REPLACE FUNCTION insert_infotable() RETURNS trigger AS $insert_infotable$
    BEGIN   
        --some irrelevant code
        IF NOT EXISTS (SELECT * FROM infotable WHERE col1 = NEW.col1 AND col2 = NEW.col2) THEN
            INSERT INTO infotable(col1, col2, col3, col4, col5, col6) values (--some values--);
        END IF;
        RETURN NEW;
    END;
$insert_infotable$ LANGUAGE plpgsql;

infotable在列col1col2上具有UNIQUE约束。

通常,一切正常,但很少,大约每千次插入一次,触发器将返回错误“表的infotable的重复键值违反了唯一约束” unique_col1_and_col2”。触发功能中有IF NOT EXISTS部分,因此不应该发生。

第一个问题是这可能是什么原因?我唯一能想到的是两个用户同时获取相同信息的比赛,都触发了触发器,但随后一个人通过触发器更新了第二张表,而第二个用户则得到了重复错误。因此,他的整个插入查询都会失败,包括对主table的插入。

如果是这种情况,我该怎么办?对于应该有100个以上的用户同时插入数据的表,使用插入锁定是个好主意吗?

如果是的话,我应该使用哪种类型的锁以及我应该锁定哪种表-主表或由触发器修改的第二个表? (或者我猜我应该在主插入语句中还是在触发器函数中拥有锁?)

1 个答案:

答案 0 :(得分:1)

是的,这是比赛条件。同时运行的两个这样的触发器不会看到彼此的修改,因为事务尚未提交。

由于您对infotable有唯一的约束,因此可以简单地使用

INSERT INTO infotable ...
ON CONFLICT (col1, col2) DO NOTHING;