标准答案
- 先定义唯一业务意图,例如用户、商品、外部请求号或幂等键的组合,并在数据库中使用唯一约束或条件写入做最终兜底。
- 请求进入后原子地创建或获取幂等记录。首次请求执行状态迁移和扣减,重复请求返回首次成功结果或明确的处理中状态。
- 扣款、库存、订单状态等关键写入应在本地事务或可恢复的状态机中完成,避免部分成功后重试产生第二次副作用。
- 外部支付或消息投递需要保存外部流水号和回调状态,通过对账、去重和补偿处理不确定结果。
- 接口超时不代表业务没有成功。调用方重试时必须携带同一幂等标识,服务端按相同业务意图返回稳定结果。
题目解析
并发重复来源很多:双击、网络重试、客户端超时、网关重放、消息至少一次投递和人工补单都可能触发。
唯一约束保证最终不会产生两个同类事实;幂等记录负责让重复调用获得一致响应;状态机负责让中间失败能够继续恢复。
代码示例
幂等记录和订单写入需要在同一可靠边界内处理,重复请求直接读取已有结果。
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 }常见误区
- 只在前端禁用提交按钮,认为就不会出现重复请求。
- 只加分布式锁,没有数据库唯一约束和异常恢复路径。
- 超时后直接创建新订单,而不是用同一幂等键查询原请求结果。