← 返回题目列表

请求幂等号和业务主键有什么区别?

中等 第 21 / 25 题 更新于 2026/09/30
幂等号业务主键分布式ID请求去重
学习 AI 实战项目

简化版

请求幂等号标识“一次业务意图”,用于识别超时重试;业务主键标识“一个已经存在的业务实体”。两者生命周期和基数不同,不能默认共用。 同一幂等号必须绑定相同调用方、操作和参数摘要,并复用首次处理结果;一个订单主键上可以先后发生创建、支付、取消等多个幂等操作。

详细版

创建订单前可能还没有 orderId,客户端先生成 requestId,服务端以 tenant + operation + requestId 建唯一记录。首次请求执行并保存结果,重复请求返回同一结果;若同号参数不同,应拒绝冲突,而不是复用错误响应。

业务主键用于外键、查询和状态机,通常长期保留;幂等记录只需覆盖最大重试窗口,但资金操作可能需要更久审计。删除幂等记录后再次到达的旧请求有重复副作用风险,因此 TTL 要从客户端、MQ 和人工补偿的最长重放时间推导。

完整版教学

一、两个 ID 标识的对象不同

用户点两次“提交订单”可能是一次意图的网络重试,也可能是真的买两件。requestId 用来区分意图,orderId 用来标识最终订单。如果直接拿 userId 或商品 ID 幂等,会错误合并用户合法的第二次购买。

记忆钩子:幂等号认“这次动作”,业务主键认“动作作用的对象”。

二、创建类请求为什么尤其需要幂等号

Client --requestId=R1--> create order
Server creates order O9, response lost
Client --requestId=R1--> retry
Server returns existing O9, not O10

第二次请求到达时,客户端尚不知道 O9,因此不能靠 orderId 去重。服务端必须在创建实体前记录 R1 或用唯一约束将 R1 与 O9 原子绑定。

三、幂等键的作用域

字段为什么需要
tenant/user防不同调用方碰撞
operation创建与取消不能互相去重
requestId标识一次意图
payloadHash防同号不同参数

唯一索引可设为 (tenant_id, operation, request_id)。只用 requestId 依赖所有客户端都生成全局唯一值,边界更脆弱。

四、处理记录的状态机

ABSENT -> PROCESSING -> SUCCEEDED
                    \-> FAILED_RETRYABLE
                    \-> FAILED_FINAL

首次请求通过唯一插入取得执行权。重复请求看到 SUCCEEDED 返回已保存结果;看到 PROCESSING 可等待、返回处理中或查询;最终失败是否允许同号重试要由接口契约定义。业务写和幂等记录最好在同一事务,或通过状态恢复消除中间空窗。

五、与业务状态机协作

订单 O9 可有 create:R1、pay:R2、cancel:R3 三个幂等动作。业务状态机还要验证 PAID 不能再次扣款、CANCELLED 不能发货。幂等去重挡相同请求,状态机挡语义上不合法的不同请求,两层都需要。

如果支付渠道也支持幂等键,应向下游传播稳定 R2,而不是每次重试生成新键,否则本地去重无法阻止下游重复扣款。

六、TTL 和安全清理

客户端最多重试 24 小时、MQ 最长保留 7 天、人工补偿可能 30 天,那么只保留 1 天幂等记录不安全。可长期保留幂等键到业务流水,或将旧记录压缩归档。删除前还要确认外部系统不会再次重放。

监控幂等命中、同号参数冲突、PROCESSING 超时、记录增长和下游重复拒绝。数据库唯一索引是并发竞态的最后防线,不能只用“先查再插”。

七、常见误区与追问

  • 误区:有 orderId 就不需要 requestId。 创建之前客户端还没有业务主键。
  • 误区:同一用户同一商品只能创建一次。 会误伤合法重复购买,幂等要按意图标识。
  • 误区:收到重复请求直接返回成功即可。 应返回首次真实结果,参数冲突则拒绝。
  • 误区:Redis SETNX 足以永久保证。 缓存过期或丢失后仍可能重复,核心写应有持久唯一约束。
  • 追问:PROCESSING 卡死怎么办? 用租约、超时扫描和业务结果查询安全接管。
  • 追问:参数摘要包含哪些字段? 包含影响副作用的规范化字段,排除 traceId 等非业务字段。
  • 追问:幂等记录能否删除? 只能在所有可能重放窗口结束后按风险清理或归档。

八、加强记忆

请求幂等号围绕“一次意图”建立,业务主键围绕“一个实体”建立。落地时用调用方+操作+requestId 定作用域,payloadHash 防同号异参,状态机保存首次结果,唯一约束解决并发。TTL 则必须覆盖最长重放来源,而不是随便设一天。