← 返回题目列表

幂等表和唯一约束在接口防重中怎么设计?

高频 中等 第 7 / 33 题 更新于 2026/07/28
数据库设计幂等唯一约束防重

简化版

接口防重常用幂等号加唯一约束实现:每个请求带业务唯一键,如订单号、支付流水号、请求号,数据库用唯一索引保证同一业务操作只成功一次。幂等表负责记录请求状态、结果和过期时间,既能防重复提交,也能支持失败重试和结果查询。

详细版

常见设计:

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 唯一,重复创建订单时撞唯一约束后查询原订单返回。

但复杂业务可能需要幂等表:

  • 一个请求影响多张表;
  • 需要保存响应快照;
  • 处理过程较长;
  • 要区分处理中、成功、失败;
  • 幂等键和业务主键不是一回事。

六、失败和超时怎么处理

最难的是请求超时。客户端不知道服务端到底成功还是失败。幂等表能让客户端重试时查到状态。

如果业务成功但更新幂等表失败,会造成状态不一致。因此幂等表和核心业务写入最好放在同一个数据库事务里,或者设计补偿任务修复状态。

面试里可以把超时讲成三种状态:业务未执行、业务执行中、业务已成功但客户端没收到响应。幂等表的意义就是把这三种状态持久化下来,让客户端第二次请求时不靠猜测,而是按 PROCESSINGSUCCESSFAILED 的记录做确定处理。

七、常见误区与追问

重复请求看到的状态推荐处理说明
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 被不同参数复用会掩盖严重错误。
  • 误区:唯一约束报错直接返回失败。 重复请求撞唯一约束后应该查询已有记录,再按状态返回,而不是简单报系统错误。
  • 追问:业务表唯一键能不能替代幂等表? 简单创建类接口可以,但多表写入、长流程、需要结果快照或处理中状态时,单独幂等表更稳。
  • 追问:幂等记录要不要过期? 要结合业务重试窗口设置,例如支付回调可能保留数天,普通提交请求可能保留几小时,过期后归档或清理。
  • 追问:成功业务写入了但幂等表没更新怎么办? 尽量放同一事务;跨系统场景要有补偿任务,通过业务单号反查并修复幂等状态。

八、加强记忆

幂等靠“业务唯一键 + 数据库唯一约束”兜底,幂等表记录请求状态、参数摘要和结果快照。重复请求不要再执行业务,而是按已有状态返回;复杂链路要处理处理中、成功、失败和超时重试。