幂等表和唯一约束在接口防重中怎么设计?
简化版
接口防重常用幂等号加唯一约束实现:每个请求带业务唯一键,如订单号、支付流水号、请求号,数据库用唯一索引保证同一业务操作只成功一次。幂等表负责记录请求状态、结果和过期时间,既能防重复提交,也能支持失败重试和结果查询。
详细版
常见设计:
idempotency_key 幂等键
biz_type 业务类型
biz_id 业务对象
status PROCESSING/SUCCESS/FAILED
request_hash 请求参数摘要
response_snapshot 响应快照
expire_at 过期时间
关键点:
- 对
(biz_type, idempotency_key)建唯一约束; - 第一次请求插入成功,继续执行业务;
- 重复请求插入失败或查到已有记录,按状态返回;
- 参数摘要不同要拒绝,避免同一个幂等键被不同请求复用;
- 业务成功后记录结果,重复请求可直接返回相同结果;
- 过期数据要清理或归档。
完整版教学
一、幂等解决的是重复请求
重复请求很常见:
- 用户重复点击提交;
- 前端超时自动重试;
- 网关重试;
- 消息队列重复投递;
- 第三方回调重复通知。
如果没有幂等控制,可能重复下单、重复扣款、重复发券、重复创建记录。
二、唯一约束是最可靠的防线
应用层先查再插,在并发下不可靠。两个请求同时查不到,然后都插入,就重复了。
数据库唯一约束能在最终写入点兜底:
unique(biz_type, idempotency_key)
无论多少并发请求,只有一个能插入成功,其他都会撞唯一约束或查到已有记录。
三、幂等表状态怎么设计
幂等记录通常不只是一个 key,还要有状态:
PROCESSING:请求正在处理;SUCCESS:处理成功,可以返回结果快照;FAILED:处理失败,可根据错误类型决定是否允许重试;EXPIRED:超过保留窗口。
如果重复请求看到 PROCESSING,可以返回处理中、短暂等待或让客户端稍后查询。看到 SUCCESS,应该返回第一次成功的结果,而不是再执行一次业务。
四、请求参数摘要为什么重要
如果客户端复用同一个幂等键,但请求参数变了,系统不能把它当成同一个业务操作。
例如第一次请求是给 A 用户发 100 元券,第二次同幂等键却变成给 B 用户发 200 元券。如果只看 key,就可能返回错误结果或掩盖严重问题。
所以要保存 request_hash,重复请求时校验参数摘要一致。
五、业务表唯一键和幂等表可以配合
有些场景不需要单独幂等表,业务表唯一键就够了。比如订单表 order_no 唯一,重复创建订单时撞唯一约束后查询原订单返回。
但复杂业务可能需要幂等表:
- 一个请求影响多张表;
- 需要保存响应快照;
- 处理过程较长;
- 要区分处理中、成功、失败;
- 幂等键和业务主键不是一回事。
六、失败和超时怎么处理
最难的是请求超时。客户端不知道服务端到底成功还是失败。幂等表能让客户端重试时查到状态。
如果业务成功但更新幂等表失败,会造成状态不一致。因此幂等表和核心业务写入最好放在同一个数据库事务里,或者设计补偿任务修复状态。
面试里可以把超时讲成三种状态:业务未执行、业务执行中、业务已成功但客户端没收到响应。幂等表的意义就是把这三种状态持久化下来,让客户端第二次请求时不靠猜测,而是按 PROCESSING、SUCCESS、FAILED 的记录做确定处理。
七、常见误区与追问
| 重复请求看到的状态 | 推荐处理 | 说明 |
|---|---|---|
PROCESSING | 返回处理中或短暂等待后查询 | 避免并发重复执行业务 |
SUCCESS | 返回第一次成功结果快照 | 保证客户端多次请求结果一致 |
FAILED | 按错误类型决定能否重试 | 参数错误不可重试,系统错误可重试 |
记忆钩子:幂等不是“挡住第二次 HTTP 请求”,而是“同一个业务操作只落库一次,并且重复请求能拿到确定结果”。
典型并发时间线如下:
T0 请求 A insert(idempotency_key=K) 成功,状态 PROCESSING
T1 请求 B insert(idempotency_key=K) 失败,查到 PROCESSING
T2 请求 A 完成业务,更新状态 SUCCESS,保存 response_snapshot
T3 请求 B 重试查询,直接返回 response_snapshot
如果一秒内有 20 次相同支付回调,唯一约束保证只有 1 次能进入核心扣款逻辑,其余 19 次都只能复用已有状态。这里真正兜底的是数据库唯一约束,而不是应用层的内存锁。
- 误区:前端按钮置灰就能防重复提交。 前端只能减少误点,挡不住刷新、重试、脚本、网关重放和第三方重复回调。
- 误区:幂等键相同就一定是同一请求。 还要校验
request_hash,否则同一个 key 被不同参数复用会掩盖严重错误。 - 误区:唯一约束报错直接返回失败。 重复请求撞唯一约束后应该查询已有记录,再按状态返回,而不是简单报系统错误。
- 追问:业务表唯一键能不能替代幂等表? 简单创建类接口可以,但多表写入、长流程、需要结果快照或处理中状态时,单独幂等表更稳。
- 追问:幂等记录要不要过期? 要结合业务重试窗口设置,例如支付回调可能保留数天,普通提交请求可能保留几小时,过期后归档或清理。
- 追问:成功业务写入了但幂等表没更新怎么办? 尽量放同一事务;跨系统场景要有补偿任务,通过业务单号反查并修复幂等状态。
八、加强记忆
幂等靠“业务唯一键 + 数据库唯一约束”兜底,幂等表记录请求状态、参数摘要和结果快照。重复请求不要再执行业务,而是按已有状态返回;复杂链路要处理处理中、成功、失败和超时重试。