接口层幂等如何设计?有哪些实现要点?
结论先行:接口幂等指"同一个请求无论执行一次还是多次,结果都一致"。GET 等查询天然幂等,需要设计的是 POST/PUT 这类会改状态的接口。核心思路是让请求携带稳定幂等键,服务端用原子手段保证"第一次真正执行,后续直接返回首次结果",常见落地是幂等令牌 + Redis 占位,或唯一索引 + 状态机校验。
一、常见实现方案
| 方案 | 原理 | 适用 |
|---|---|---|
| 幂等令牌 | 前端请求先取 token,处理时原子占用,用后即焚 | 表单提交、下单防重复 |
| 唯一约束 | 业务幂等键建唯一索引,插入冲突视为重复 | 流水、消息落库 |
| 状态机 | 只允许合法状态流转,非法流转直接返回成功 | 订单状态变更 |
| 乐观锁 | UPDATE 带版本/状态条件,0 行影响即重复 | 并发更新 |
二、核心流程与注意点
- 幂等键必须来自业务:订单号、支付流水号、用户请求号,而不是随机生成的无关字符串;
- 占用要原子:先查再写有竞态,必须用 Redis SETNX 或数据库唯一索引一次写入判断;
- 键要过期兜底:Redis 占位设置合理 TTL,防止客户端一直不提交导致键永久占用;
- 重复请求返回首次结果:处理成功后缓存响应,重试时直接返回,避免下游重复扣款;
- 超时重试用同一幂等键:网关或 SDK 层重试必须复用原请求标识;
- 网关层聚合:SDK 或网关可自动为写请求生成并透传幂等键,业务侧无需每个接口重复实现;
- 边界说明:幂等只约束同一个键的重复请求,不同键的请求并发修改同一资源时仍需配合乐观锁。
三、代码示例
-- 状态机校验:只有 CREATED 才能流转到 PAID,0 行影响说明已处理
UPDATE t_order SET status = 'PAID' WHERE id = ? AND status = 'CREATED';
# 幂等令牌:抢到令牌的请求才允许执行业务
redis-cli SETNX idem:order:1001 token-abc123
redis-cli EXPIRE idem:order:1001 60
常见追问 / 记忆点
- 追问:幂等与并发控制是一回事吗?答:不是,幂等解决"同一请求重复执行"的副作用,并发控制解决"多个不同请求同时改同一数据"。
- 追问:先查订单状态再决定是否处理够吗?答:不够,查与改之间仍有竞态,必须靠唯一约束或乐观锁兜底。
- 追问:幂等键有效期怎么设?答:覆盖客户端最长重试周期即可,过短会漏判重复,过长会占用存储。
- 记忆点:业务幂等键 + 原子占用 + 状态校验 + 响应缓存,四步构成完整幂等设计。