幂等更新的核心是多次执行相同操作结果与一次一致,需依托主键/唯一约束防重、条件更新(如WHERE status=’unpaid’)、版本号/时间戳校验、upsert原子操作,避免应用层竞态。

幂等更新的核心是:同一条数据多次执行相同更新操作,结果与执行一次完全一致,不会因重复提交导致数据错乱或冲突。
用主键或唯一约束做天然防重
确保表中存在明确的业务主键(如订单号、用户ID)或添加唯一索引(如 UNIQUE (order_no))。这是幂等的前提——数据库层面拒绝重复插入,避免脏数据。
- 插入时用
INSERT ... ON CONFLICT DO NOTHING/UPDATE(postgresql) - mysql 可用
INSERT INTO ... ON DUPLICATE KEY UPDATE - 避免仅依赖应用层判断是否存在,防止并发时的“检查-插入”时间差漏洞
更新语句自带条件过滤
不直接写 UPDATE table SET status = 'paid' WHERE order_id = '123',而是带上业务状态前置条件:
UPDATE orders SET status = 'paid', updated_at = NOW() WHERE order_id = '123' AND status = 'unpaid'- 这样即使重复执行,只有第一次能生效;后续执行影响行为为 0 行,安全可控
- 适合状态机类场景(如待支付→已支付→已完成),天然具备幂等性
引入版本号或时间戳字段控制并发更新
在表中增加 version(整型)或 updated_at(时间戳)字段,更新时校验是否为预期值:
UPDATE items SET price = 99.9, version = version + 1 WHERE id = 1001 AND version = 5- 若返回影响行数为 0,说明已被其他请求抢先更新,可选择重试或报错提示
- 比乐观锁更轻量,无需额外 select,一次 SQL 完成校验与更新
用 upsert 替代先查后更逻辑
放弃“SELECT 判断是否存在 → INSERT/UPDATE”的两步操作。这类逻辑在高并发下极易出现竞态条件(race condition)。
- 统一使用 upsert(insert on conflict)语义,把判断和写入交给数据库原子执行
- 例如同步用户资料:以
user_id为冲突键,ON CONFLICT (user_id) DO UPDATE SET name = EXCLUDED.name, email = EXCLUDED.email - 既保证最终一致性,又消除应用层复杂的状态判断逻辑
幂等不是靠代码层层防御,而是靠数据库约束、SQL 条件和原子操作共同构建。设计时优先让 DB 做事,而不是让应用猜状态。