标准答案

  1. 先定义唯一业务意图,例如用户、商品、外部请求号或幂等键的组合,并在数据库中使用唯一约束或条件写入做最终兜底。
  2. 请求进入后原子地创建或获取幂等记录。首次请求执行状态迁移和扣减,重复请求返回首次成功结果或明确的处理中状态。
  3. 扣款、库存、订单状态等关键写入应在本地事务或可恢复的状态机中完成,避免部分成功后重试产生第二次副作用。
  4. 外部支付或消息投递需要保存外部流水号和回调状态,通过对账、去重和补偿处理不确定结果。
  5. 接口超时不代表业务没有成功。调用方重试时必须携带同一幂等标识,服务端按相同业务意图返回稳定结果。

题目解析

并发重复来源很多:双击、网络重试、客户端超时、网关重放、消息至少一次投递和人工补单都可能触发。

唯一约束保证最终不会产生两个同类事实;幂等记录负责让重复调用获得一致响应;状态机负责让中间失败能够继续恢复。

代码示例

幂等记录和订单写入需要在同一可靠边界内处理,重复请求直接读取已有结果。

TypeScript
const record = await idempotencyStore.createIfAbsent({ key, userId, payloadHash })

if (!record.created) {
  return record.response ?? { status: "processing" }
}

const order = await db.transaction(async (tx) => {
  const created = await tx.orders.insertWithUniqueBusinessKey(input)
  await tx.idempotency.saveResponse(key, { orderId: created.id })
  return created
})

return { orderId: order.id }

常见误区

  • 只在前端禁用提交按钮,认为就不会出现重复请求。
  • 只加分布式锁,没有数据库唯一约束和异常恢复路径。
  • 超时后直接创建新订单,而不是用同一幂等键查询原请求结果。

作者信息