API 幂等性设计指南(免费版)
一、它解决什么问题
同一操作被触发多次的场景非常普遍:
- 客户端超时后自动重试,但首次请求其实已成功;
- 用户重复点击"提交/支付"按钮;
- 消息队列消息重复投递(at-least-once)。
没有幂等保护时,会出现重复下单、重复扣款、重复发货。
幂等性:同一请求执行一次与执行 N 次,对系统产生的结果相同。
二、幂等键机制
客户端为每个"业务意图"生成唯一键,通过请求头传递:
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
- 键在客户端生成(建议 UUID),同一业务操作的重试必须复用同一个键;
- 不同业务意图(重新下单、改了金额)必须用新键;
- 键的作用域通常是"客户端 + 接口 + 用户"。
三、服务端三态处理
| 请求情形 | 服务端动作 | 返回 |
|---|---|---|
| 首次见到该键 | 执行业务,缓存结果 | 201 Created(创建类) |
| 键已有完成结果 | 不重复执行,返回首次结果 | 200 OK(重放) |
| 同键正在处理中 | 不并发执行,提示稍后重试 | 409 Conflict |
请求到达
├─ 键在完成缓存? ──是──► 重放首次结果(200)
├─ 键在处理中? ──是──► 冲突(409)
└─ 都不是 ──► 标记处理中 → 执行 → 缓存结果(201)
四、结果缓存与 TTL
- 必须缓存完整响应(状态码 + 响应体 + 关键响应头),重放时原样返回;
- 缓存设 TTL(常取 24 小时),覆盖客户端最长重试窗口即可;
- 重放响应可加头
Original-Date,便于客户端识别这是重放结果。
五、in-flight 并发与失败处理
- 首次请求执行期间,键处于 in-flight,并发同键请求返回 409,避免双写;
- 业务执行失败时必须释放 in-flight 锁,允许后续同键重试;
- 只有业务成功完成才写入结果缓存,失败结果不应缓存(否则无法重试)。
六、天然幂等 vs 需要幂等键
- 天然幂等:GET/PUT/DELETE(对同一资源重复操作结果相同);
- 需要幂等键:POST 创建、支付、退款、发券等非幂等操作;
- 数据库层可配合唯一索引(业务单号唯一)作为最后防线。
七、落地检查清单
- [ ] 写操作要求客户端传 Idempotency-Key,缺失时按策略拒绝或生成
- [ ] 重试复用同一键;新业务意图用新键
- [ ] 完成结果完整缓存,重放原样返回(200 vs 201 语义正确)
- [ ] in-flight 同键并发返回 409,不双写
- [ ] 业务失败释放锁、不缓存失败结果,允许重试
- [ ] 缓存有 TTL,覆盖重试窗口
- [ ] 关键表有业务唯一索引兜底
- [ ] 重放、冲突、键缺失分别记录日志
八、硬性规则
- 幂等键绑定"业务意图",重试复用、意图变更换新键。
- 只有成功结果才缓存;in-flight 必须防并发双写,失败必须释放。
- 重放返回首次的完整结果,不得重新执行业务副作用。
- 本指南为设计方法;需要可直接调用的幂等存储引擎,请使用专业版。