无法复现‘CREATE IF NOT EXISTS’SQL的主键冲突错误求助
点赞操作主键冲突问题分析
问题背景
我用以下SQL语句实现用户点赞的“存在则不插入”逻辑,持久化到Azure SQL数据库:
if not exists( select 1 from likes where UserId = @UserId and EntityId = @EntityId and EntityType = @EntityType) insert into likes (UserId, EntityType, EntityId) values (@UserId, @EntityType, @EntityId)
Likes表无Id列,主键为(UserId, EntityType, EntityId)。直接查询和API测试都正常,点赞已存在时数据库无变化,但近期出现主键冲突错误:
Violation of PRIMARY KEY constraint 'PK_Likes'. Cannot insert duplicate key in object 'dbo.Likes'. The duplicate key value is (19, 1, 113). The statement has been terminated.
无法复现该错误,请求分析原因。
可能的原因
- 并发写入竞争:这是最核心的原因。当多个相同的点赞请求同时抵达数据库时,两个请求会先后执行
IF NOT EXISTS查询——此时都查不到对应记录,接着同时执行INSERT操作,就触发了主键冲突。因为IF NOT EXISTS和INSERT是两个独立操作,中间存在时间窗口,并发场景下会绕过检查逻辑。 - 事务隔离级别导致的不可重复读:默认的
READ COMMITTED隔离级别下,一个事务执行INSERT后还未提交,另一个事务的IF NOT EXISTS查询看不到这条未提交的记录,就会重复插入。 - Azure SQL的瞬时同步延迟:极端情况下,Azure SQL的节点间同步、日志写入存在短暂延迟,可能导致刚插入的记录无法被紧接着的
IF NOT EXISTS查询读取到,进而触发重复插入。 - 客户端重复请求:前端按钮防抖失效、网络重试机制触发,导致同一用户的点赞请求被多次发送,且这些请求刚好落在数据库的检查时间窗口内。
解决方案建议
- 改用原子性的MERGE语句:把
IF NOT EXISTS和INSERT合并成一个原子操作,彻底避免并发竞争:
MERGE INTO likes AS target USING (SELECT @UserId AS UserId, @EntityType AS EntityType, @EntityId AS EntityId) AS source ON (target.UserId = source.UserId AND target.EntityType = source.EntityType AND target.EntityId = source.EntityId) WHEN NOT MATCHED THEN INSERT (UserId, EntityType, EntityId) VALUES (source.UserId, source.EntityType, source.EntityId);
- 前端添加防抖处理:给点赞按钮加防抖,比如1秒内只允许触发一次请求,避免用户快速点击导致重复提交。
- API层捕获异常:在代码里捕获主键冲突的异常,返回“您已点赞过”的提示即可,不影响正常业务流程。
- 调整事务隔离级别(谨慎使用):可以尝试将事务隔离级别设为
REPEATABLE READ,但这会增加锁竞争,可能影响数据库性能,需要根据实际业务场景权衡。
内容的提问来源于stack exchange,提问作者user3795600
相关产品推荐
相关产品推荐

