← 返回分布式系统
🔁 弹性治理 · 面试双考点

重试与幂等

分布式调用一定会失败。重试让系统更健壮,但重试会带来重复执行。幂等则保证"重复执行 = 执行一次"。两者必须配套,缺一不可。

🎯为什么需要重试

网络抖动、服务短暂过载、GC 停顿都会导致调用失败。多数失败是瞬时的,稍等再试往往就成功了。重试是分布式容错的基本手段。

但:重试本身会带来副作用——重复请求。如果"扣款"被重试两次,用户就被扣了两笔。所以有重试就必须有幂等。
🧭
一句话:重试解决"暂时性失败",幂等解决"重试带来的重复"。它们是容错的一体两面。

🔁重试策略

策略说明注意
固定间隔每 N 秒重试一次简单,但易加重对端的压力
指数退避间隔按 2^n 增长(1s,2s,4s…)给对端恢复时间,推荐
随机抖动退避时间加随机扰动避免"重试风暴"同步(见下)
最大次数超过 N 次放弃并降级必须设上限,否则死循环
✅
最佳实践:指数退避 + 随机抖动 + 最大次数上限。只对可重试错误(网络超时、503)重试,对业务错误(参数错、余额不足)直接失败,不要重试。

⛈️重试风暴(Retry Storm)

当上游集体重试、且重试间隔一致时,会形成"同步重试潮",周期性把下游打成过载,越重试越糟。

上游A 上游B 上游C 同步重试 下游(被周期打爆) 随机抖动打破同步
⚠️
解法:① 重试间隔加随机抖动,错开重试波峰;② 下游做限流熔断(见 rate-limiting);③ 上游对失败做熔断(见 distributed-resilience-patterns 的熔断),别无脑重试。

🔒幂等设计(核心)

幂等 = 同一操作执行一次和执行多次,效果相同。关键:用"唯一标识"识别"这是不是同一个请求"。

常见幂等方案

① 唯一请求号(Idempotency-Key):调用方每次生成唯一 ID,服务端用表/Redis 记录"已处理过的 ID",重复直接返回原结果。

② 业务唯一键 + 去重表:如订单号唯一,插入前查重。

③ 状态机:业务有终态(已支付),重复消息命中终态直接忽略。

④ 乐观锁 / 版本号:UPDATE ... WHERE version=旧值,并发只一个成功。

✅
关键:幂等要在接口设计阶段就考虑,而不是事后补。最好的幂等是"天然幂等"——如"把余额设为 100"比"余额+100"天然幂等。

🤝重试 + 幂等的配套实践

调用方:只对瞬时错误重试,用"指数退避 + 抖动 + 上限",并携带唯一幂等键。
服务提供方:用幂等键/唯一约束识别重复请求,重复直接返回(不重复执行)。
下游保护:服务端配合限流、熔断,防止重试把自身压垮。
可观测:记录重试次数、幂等命中数,便于定位"重试是否合理"。
🧩
与 MQ 呼应:消息队列里"at-least-once + 幂等"本质就是这一套思想——允许重复投递,靠消费侧幂等保证正确。详见 mq/message-queue。

🎯面试要点速记

重试目的:应对瞬时失败,提升成功率。
策略:指数退避 + 随机抖动 + 最大次数;只对可重试错误重试。
重试风暴:同步重试潮打爆下游;用抖动+限流+熔断化解。
幂等:执行多次=一次;靠唯一键/去重表/状态机/乐观锁实现。
天然幂等:"设为"比"加"幂等;设计阶段就要考虑。
配合:重试带幂等键 + 服务端幂等 + 下游限流熔断。
🔥
必考题:"如何保证重试不会重复扣款?"——调用方生成唯一幂等键随请求带上;服务端用唯一约束/去重表记录,重复请求直接返回首次结果,不重复执行业务。必要时结合状态机(已支付则忽略)。