sql幂等写入要求同一条数据多次写入结果与一次相同,核心是重复时无副作用。方案包括:1. 唯一约束插入(insert ignore/on duplicate key update);2. 主键或版本号更新(where校验状态或version);3. 防重表兜底;4. 应用层请求标识+幂等key+redis预校验。

SQL幂等写入的核心是:同一条数据多次执行写入操作,结果与只执行一次完全一致。关键不在于“避免重复”,而在于“重复时无副作用”。实现方式取决于业务场景和数据库能力,下面分几种常见方案说明。
基于唯一约束的插入幂等
最简单可靠的方案,适用于新增场景。在业务字段(如订单号、用户ID+时间戳)上建立唯一索引或主键约束。
- 使用 INSERT IGNORE(MySQL):冲突时静默跳过,不影响事务,返回影响行数为 0
- 使用 ON DUPLICATE KEY UPDATE(MySQL):冲突时更新指定字段(如 status = VALUES(status)),适合需记录“最后尝试时间”等轻量覆盖
- 使用 INSERT ... ON CONFLICT DO NOTHING / DO UPDATE(PostgreSQL):语义清晰,支持复杂条件判断
基于主键/版本号的更新幂等
适用于修改已有记录的场景。不能仅靠 WHERE 条件判断是否存在,而要确保“改了等于没改”或“改了也安全”。
- 用主键精准定位:UPDATE table SET status='paid' WHERE id=123 AND status != 'paid' —— 避免重复更新已生效的状态
- 引入 version 字段:UPDATE table SET status='paid', version=version+1 WHERE id=123 AND version=5;失败说明已被其他请求抢先更新
- 用时间戳或状态机校验:UPDATE order SET updated_at=NOW() WHERE id=123 AND status IN ('created', 'processing') —— 限定合法变更路径
基于业务单据号的防重表(兜底方案)
当无法加唯一索引(如字段太长、组合多变、历史数据不洁),可单独建一张轻量防重表(dedup_log),结构通常为:(biz_type, biz_id, create_time),其中 (biz_type, biz_id) 建唯一索引。
- 写入前先 INSERT IGNORE 到防重表;成功则继续主逻辑,失败则直接返回“已存在”
- 配合 TTL 或定时清理,避免无限膨胀(如保留 30 天)
- 注意:该表需与主业务表在同一事务或强一致性机制下,否则存在微小窗口期
应用层配合:请求标识 + 幂等Key提取
数据库只是执行者,真正的幂等控制起点在入口。每次外部请求应携带唯一标识(如 X-Idempotency-Key),服务端统一提取并映射为业务幂等 Key(如 payment_20240501_ORD123)。
- 幂等 Key 应具备业务意义且稳定:不依赖随机值、不随请求参数微调而变化
- 建议缓存层(Redis)预校验:SETNX key expire_time;命中则直接返回上次结果(需存储响应快照或状态)
- 注意异常分支:网络超时后客户端重试,服务端需识别并返回原始结果,而非报错或新建










