Postgres并发问题

时间:2013-11-07 11:11:06

标签: postgresql

我编写了以下触发器,以确保插件上的字段'filesequence'始终为一个利益相关者接收最大值+ 1.

CREATE OR REPLACE FUNCTION update_filesequence()
        RETURNS TRIGGER AS '
  DECLARE
    lastSequence file.filesequence%TYPE;

  BEGIN
    IF (NEW.filesequence IS NULL) THEN

      PERFORM ''SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE'';

      SELECT max(filesequence) INTO lastSequence FROM file WHERE stakeholder = NEW.stakeholder;

      IF (lastSequence IS NULL) THEN
        lastSequence = 0;
      END IF;

      lastSequence = lastSequence + 1;

      NEW.filesequence = lastSequence;
    END IF;
    RETURN NEW;
  END;
' LANGUAGE 'plpgsql';

CREATE TRIGGER file_update_filesequence BEFORE INSERT
  ON file FOR EACH ROW EXECUTE PROCEDURE
  update_filesequence();

但我在数据库上重复'filesequence':

select id, filesequence, stakeholder from file where stakeholder=5273;
id      filesequence    stakeholder
6773    5               5273
6774    5               5273

通过我的承诺,SELECT ... FOR UPDATE将锁定同一个利益相关者的两个事务,然后第二个将读取新的“filesequence”。但它没有用。

我对PgAdmin进行了一些测试,执行以下操作:

BEGIN;
select id from stakeholder where id = 5273 FOR UPDATE;

它真的锁定了其他记录被插入同一个利益相关者。然后似乎LOCK正在工作。

但是当我使用并发上传运行应用程序时,我会看到重复。

有人可以帮助我找到触发器的问题吗?

谢谢, 道格拉斯。

1 个答案:

答案 0 :(得分:1)

你的想法是对的。要根据另一个字段获取自动增量(让它说它指定给一个组)你不能使用序列,那么你必须在增加它之前锁定该组的行。

触发器功能的逻辑就是这样。但是您对PERFORM操作有误解。它应该被放置而不是SELECT关键字,因此它不会接收字符串作为参数。这意味着当你这样做时:

PERFORM 'SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE';

PL / pgSQL实际上正在执行:

SELECT 'SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE';

忽略了结果。

你需要做的就是:

PERFORM id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE;

就是这样,只改变这一行,你就完成了。