You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

删除后插入操作偶现唯一键冲突失败问题排查

解决先删后插导致的唯一键冲突问题

兄弟,这个问题我太熟了——你碰到的是并发场景下的竞态条件导致的唯一键冲突,而且大概率是多个请求同时操作同一个appointment_id时触发的。我给你拆解下原因和解决方案:

问题根源

举个具体场景就能明白:

  • 请求A和请求B同时要更新appointment_id=1001的记录
  • 请求A先执行DELETE,成功删除了这条记录,但还没执行INSERT
  • 这时候请求B也执行DELETE,因为A已经删了,所以B的删除也成功
  • 接着请求A完成INSERT,成功写入新记录
  • 最后请求B执行INSERT,就会触发appointment_id的唯一键冲突,操作失败

这种情况在并发量上去之后就会偶尔出现,因为先删后插不是原子操作,中间有时间窗口被其他请求钻空子。

最优解决方案:用原子操作替代先删后插

直接用MySQL的INSERT ... ON DUPLICATE KEY UPDATE语法,把删除+插入的两步操作变成一个原子操作,从根源上避免竞态条件。

示例代码:

INSERT INTO appointment_primary_teams (appointment_id, team_id)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE team_id = VALUES(team_id);

逻辑说明:

  • 如果目标appointment_id不存在,就直接插入新记录
  • 如果目标appointment_id已经存在,就更新对应的team_id字段
  • 整个操作是数据库层面的原子操作,不会被其他请求打断,彻底解决并发冲突

备选方案:加行级锁强制串行化操作

如果业务上必须要先删后插(比如有其他关联逻辑),可以在操作前对目标行加排他锁,确保同一时间只有一个请求能操作这条记录。

示例代码(需要在事务中执行):

-- 开启事务
START TRANSACTION;

-- 锁定目标行,其他请求必须等待锁释放才能操作
SELECT appointment_id FROM appointment_primary_teams WHERE appointment_id = ? FOR UPDATE;

-- 执行删除和插入
DELETE FROM appointment_primary_teams WHERE appointment_id = ?;
INSERT INTO appointment_primary_teams (appointment_id, team_id) VALUES (?, ?);

-- 提交事务
COMMIT;

注意事项:

  • 必须在事务中执行锁操作,否则锁会立即释放
  • 要注意事务的超时时间,避免长时间持有锁导致死锁或者性能问题
  • 这种方案会降低并发性能,所以优先推荐第一种原子操作方案

内容的提问来源于stack exchange,提问作者Nicholas Summers

火山引擎 最新活动