问题是:每天我们都会收到很多想要添加到库存中的零件。我们通过从中读取的队列(使用4个不同的服务器)获取消息。队列总是包含元素,因此服务器可以尽可能快地读取。我们希望服务器在文章退出时简单地更新文章,如果不退出则插入它。
我们的第一个天真的解决方案只是选择看看文章是否存在,如果不存在,我们想插入。然而,由于没有行可供我们锁定,我们遇到了两个服务器同时进行选择、一无所获,然后试图插入的问题。当然,其中一个给了我们一个重复的密钥例外。
因此,我们转而查看merge语句。我们做了一个合并声明,看起来像这样(为了清晰起见,简化了):
MERGE INTO articles sr
USING (
VALUES (:PARAM_ARTICLE_NUMBER))
AS v(ARTICLE_NUMBER)
ON sr.ARTICLE_NUMBER = v.ARTICLE_NUMBER
WHEN MATCHED THEN
UPDATE SET
QUANTITY = QUANTITY + :PARAM_QUANTITY
ARRIVED_DATE = CASE WHEN ARRIVED_DATE IS NULL
THEN :PARAM_ARRIVED_DATE
ELSE ARRIVED_DATE END
WHEN NOT MATCHED THEN
INSERT (QUANTITY, ARRIVED_DATE)
VALUES (:PARAM_QUANTITY, CURRENT_TIMESTAMP);
然而,由于某些原因,我们仍然会遇到重复的关键问题。我认为,即使合并语句是原子的,两个合并语句也可以同时运行并进行选择。
除了锁定整张表之外,还有什么方法可以确保我们只得到一个插入吗?
在类似的情况下,运行具有可重复读取隔离级别的MERGE
解决了我们的问题。RS是不够的,因为它仍然允许幻影行,这正是您正在经历的问题。您可以简单地在语句末尾添加WITH RR
,然后进行尝试。
我们的测试套件运行时最多有1000个同时连接,我们认为并发性不会受到仅用于该特定语句的RR隔离的太大影响。
先插入,如果抛出,则捕获重复键异常;然后改为更新。
Charles