PostgreSQL - 重复唯一键

时间:2011-07-21 05:38:03

标签: postgresql concurrency unique-key

在我的桌子上,我有一个标有md5的辅助唯一键。在插入之前,我检查MD5是否存在,如果不存在,请插入它,如下所示:

-- Attempt to find this item
SELECT INTO oResults (SELECT domain_id FROM db.domains WHERE "md5"=oMD5);

IF (oResults IS NULL) THEN

    -- Attempt to find this domain
    INSERT INTO db.domains ("md5", "domain", "inserted") 
        VALUES (oMD5, oDomain, now());

    RETURN currval('db.domains_seq');

  END IF;

这适用于单线程插入,我的问题是当我有两个外部应用程序同时调用我的函数时碰巧具有相同的MD5。我最终得到的情况是:

应用1:看到MD5不存在

应用2:将此MD5插入表

App 1:转到现在将MD5插入到表中,因为它认为它不存在,但是因为它看到它没有出现错误,所以App 2插入它。

有更有效的方法吗?

我可以在插入时捕获错误,如果是,则选择domain_id?

提前致谢!


这似乎也包含在Insert, on duplicate update in PostgreSQL?

1 个答案:

答案 0 :(得分:2)

你可以继续尝试插入MD5并捕获错误,如果你遇到“唯一约束违规”错误然后忽略它并继续,如果你得到一些其他错误然后拯救。这样你就可以将重复检查向下推到数据库,你的竞争条件就会消失。

这样的事情:

  • 尝试插入MD5值。
    • 如果您遇到唯一的违规错误,请忽略它并继续。
    • 如果您遇到其他错误,请挽救并投诉。
    • 如果您没有收到错误,请继续。
  • 执行SELECT INTO oResults (SELECT domain_id FROM db.domains WHERE "md5"=oMD5)提取domain_id

可能会有一点性能损失,但“正确且有点慢”优于“快速但破损”。

最终,您可能会遇到更多成功插入的例外情况。然后你可以尝试在表中插入引用(通过外键)你的db.domains并在那里捕获FK违规。如果您遇到FK违规,请在db.domains上执行旧的“插入并忽略唯一违规”,然后重试导致FK违规的插入内容。这是一个基本的想法,它只是选择哪一个可能会抛出最少的例外并与之相关。