PostgreSQL MERGE语句的WHEN NOT MATCHED子句是否线程安全?
PostgreSQL 15 MERGE语句在无唯一约束下的并发竞态问题分析
核心结论
在未给(book_id, user_id)创建唯一约束的前提下,你的MERGE语句无法阻止并发事务插入相同条件的行,WHEN NOT MATCHED子句存在竞态条件。
原因分析
MERGE的执行流程分为两步:
- 基于
ON子句的条件(bb.book_id = source.book_id AND bb.user_id = source.user_id)扫描现有数据,判断是否存在匹配行; - 根据匹配结果执行
INSERT或UPDATE操作。
在没有唯一约束的场景下:
- 当两个并发事务同时执行这条MERGE时,它们都会在扫描阶段发现表中没有对应
(book_id, user_id)的行; - 随后两个事务都会进入
WHEN NOT MATCHED分支执行插入操作,最终导致表中出现两条book_id和user_id完全相同的重复记录。
PostgreSQL不会在无唯一约束的情况下,自动对ON子句涉及的条件范围加排他锁来阻止并发插入,因此无法避免这种竞态。
解决方案
要彻底解决这个问题,必须为borrowed_books表添加(book_id, user_id)的唯一约束:
ALTER TABLE borrowed_books ADD CONSTRAINT unique_book_user UNIQUE (book_id, user_id);
添加约束后:
- 当并发事务执行MERGE时,其中一个事务会成功插入数据;
- 另一个事务会触发唯一约束冲突错误,此时可以通过捕获错误并重试的逻辑来保证最终数据的唯一性。
补充说明
MERGE语句仅会对扫描到的匹配行加锁,如果没有匹配行则不会持有任何锁,因此无法阻止其他事务插入相同条件的新行。唯一约束是PostgreSQL中保证并发场景下数据唯一性的可靠机制,底层会通过锁和约束检查来避免重复插入。
内容的提问来源于stack exchange,提问作者Xiaojian Wang
相关产品推荐
相关产品推荐

