← 返回题目列表

什么是幂等性?分布式系统中如何保证幂等?

高频 中等 第 8 / 27 题 更新于 2026/07/28
幂等去重重试

简化版

幂等性指同一个操作执行一次和执行多次,对系统产生的最终影响相同。分布式系统里超时、重试、MQ 重投、用户重复点击都很常见,所以写接口必须幂等,尤其是下单、扣款、扣库存。常见方案有业务唯一键、唯一索引、去重表、Token、状态机、乐观锁和原子操作。

详细版

幂等设计的关键是找到“同一个请求”的稳定标识,比如订单号、支付流水号、请求 ID、消息 ID。然后用可靠机制阻止重复副作用:

方案原理适用场景
唯一索引对业务唯一键建唯一约束防重复创建订单、流水
去重表处理前插入 request_id,重复插入失败MQ 消费、回调处理
Token提交前发一次性 token,提交时原子校验删除表单重复提交
状态机只允许合法状态流转订单支付、退款、发货
乐观锁更新时带 version 或条件防重复更新、并发更新
Redis 原子操作SET NX、Lua 校验删除短期请求去重

注意“先查有没有,再插入/更新”不是可靠幂等,因为并发下两个请求可能同时查到没有。幂等判断要落在唯一约束、条件更新、Lua、事务等原子机制上。

完整版教学

一、为什么分布式系统离不开幂等

分布式调用最大的特点是结果不确定。你调用支付服务超时,可能支付服务没收到,也可能扣款成功但响应丢了。为了避免误判失败,调用方通常会重试;MQ 为了保证消息不丢,也常采用至少投递一次,这意味着消费者可能收到重复消息。重试和重投是可靠性的朋友,却是重复副作用的来源。

如果接口不幂等,重试一次可能变成重复扣款、重复发货、重复创建订单。幂等就是给重试兜底,让系统敢重试,又不会重复产生业务影响。

二、幂等关注的是副作用,不是返回值

同一个请求第一次返回“创建成功”,第二次返回“订单已存在”,这两个返回值不同,但系统只有一张订单,没有重复扣款,所以仍然是幂等。幂等判断的是最终状态和副作用,不要求每次响应文本完全一样。

这点很重要,因为很多接口第二次调用时天然无法返回完全一样的上下文。工程上只要第二次不会再次改变业务状态,通常就可以认为幂等。

三、幂等 key 必须稳定且有业务含义

没有幂等 key,就不知道两个请求是不是同一个操作。随机 UUID 如果每次重试都重新生成,就没有去重效果;时间戳也不适合作为幂等 key。正确做法是让客户端或上游为一次业务操作生成稳定 ID,比如 orderNo、paymentNo、 equestId、messageId,重试时携带同一个值。

对于支付回调,要用第三方交易号或商户订单号;对于 MQ 消费,要用消息 ID 或业务事件 ID;对于下单,要用订单号或提交 token。

四、几种常用方案怎么落地

唯一索引是最硬的兜底。订单表对 order_no 建唯一索引,重复插入会失败,服务捕获唯一键冲突后查询并返回已有订单。它并发安全,因为约束在数据库里。

去重表适合消息消费。消费者先在 processed_message 表插入 message_id,插入成功才执行业务,插入失败说明处理过。关键是去重记录和业务更新最好放在同一个本地事务里,否则可能业务成功但去重记录失败,下一次又重复执行。

Token 适合防重复提交。用户进入提交页先申请 token,服务端存 Redis;提交时用 Lua 原子判断 token 是否存在并删除,只有删除成功的请求继续处理。不能先 GET 再 DEL,因为并发下两个请求都可能通过 GET。

状态机适合订单类业务。订单只能从待支付到已支付,再到已发货。重复支付回调到来时,发现订单已经已支付,就直接返回成功,不再重复发积分或发消息。

五、分布式锁不是幂等的替代品

分布式锁能让同一时刻只有一个请求处理,但锁释放后,重复请求再次到来仍可能再次执行。它解决的是并发互斥,不解决历史去重。正确设计通常是锁 + 状态/去重配合:锁降低并发冲突,去重表或状态机保证重复请求不产生第二次副作用。

六、面试常见坑

先查后写是最典型坑:两个请求同时查都没有,然后都插入成功。另一个坑是幂等记录和业务操作不在同一个事务中,导致中间失败后重复执行。还有一个坑是幂等 key 粒度过粗或过细,过粗会误杀不同请求,过细会挡不住重复请求。

七、常见误区与追问

这道题不能只背概念,要把「幂等性」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。

回答层次要讲清的内容容易漏掉的边界
核心结论幂等表示同一请求执行一次和执行多次的业务结果一致,是重试、消息消费和回调的安全底座不要停在名词解释
流程机制客户端生成唯一请求号 -> 服务端检查去重记录或状态 -> 未处理则执行业务并落去重 -> 重复请求直接返回历史结果 -> 失败状态可按状态机补偿说明触发方、参与方、状态变化和兜底
工程取舍同一个 payRequestId 重试 3 次,只能扣款成功 1 次,后两次应返回已处理结果分布式基础题不能只背概念,必须落到网络不可靠、节点会故障、数据有副本这些前提
幂等性 面试拆解:
1. 客户端生成唯一请求号
2. 服务端检查去重记录或状态
3. 未处理则执行业务并落去重
4. 重复请求直接返回历史结果
5. 失败状态可按状态机补偿

记忆钩子:先定义问题,再说明一致性、可用性、分区、复制、时钟和故障模型的取舍;回答时要紧扣「幂等性」这道题,不要把相邻概念混成一段泛泛的分布式套话。

  • 误区:幂等就是接口不报错。 幂等关注业务结果一致,不是简单吞异常。
  • 误区:只有支付需要幂等。 下单、发券、扣库存、消息消费、任务调度都需要。
  • 误区:前端防重复点击就够了。 网络重试、MQ 重投和服务端超时仍会造成重复请求。
  • 追问:常见幂等方案有哪些? 唯一索引、去重表、业务状态机、版本号、Token 机制。
  • 追问:幂等键怎么选? 选业务唯一请求号,如订单号、支付流水号、消息 ID。
  • 追问:幂等和分布式锁有什么区别? 锁控制并发进入,幂等保证重复执行结果一致。

八、加强记忆

幂等就是让同一业务操作重复执行也只产生一次副作用。分布式里因为超时、重试、MQ 重投无法避免,所以写接口必须幂等。核心前提是稳定业务唯一标识,核心手段是唯一索引、去重表、Token、状态机、乐观锁和原子操作。记住两条底线:先查后写不可靠,分布式锁不等于幂等。