返回知识库

数据与服务 · 运行与可靠性

幂等性Idempotency

同一个业务意图重复到达时,服务端按幂等键只执行一次副作用,重复请求返回第一次存储的结果。

同一个支付意图被发送两次,只扣一次

点「支付 ¥100」发起一笔支付,连点两次模拟用户双击或网络重发。带上 Idempotency-Key 时,服务端把第一次的结果按 key 存下,重复请求返回存储的结果,余额只降一次;关掉 key 再连点,POST 默认不幂等,每次都扣。

账户余额¥300
Idempotency-Keypay-104

连点「支付 ¥100」= 用户双击或网络重发;「换新意图」会生成新的 key,代表一笔新的业务意图。

服务端 · key → 第一次结果

还没有已记录的 key;首次处理后会把结果存到这里。

  • 还没有请求;点「支付 ¥100」开始。

余额 ¥300。下一步:点「支付 ¥100」发起支付;连点两次模拟用户双击或网络重发,看余额是否只降一次。

幂等的三个要点,对应面板里刚发生的事

面板里的余额、key 存储和请求日志,对应幂等机制的三个工程要点。

  • 重复只执行一次副作用:同一个 key 第二次到达时不再扣款,余额不变;日志标为「重放·未扣款」。
  • key 由客户端生成、服务端按 key 存第一次结果:面板右侧的 key 存储逐条填充,重复请求命中已存行,返回同一个 paymentId。
  • 重放返回结果,不是拒绝:重复请求仍回 HTTP 200 和首次的 paymentId,客户端可以安全重试,不会因为被拒而丢失意图。

HTTP 方法:哪些天生幂等,哪些要额外保护

规范约定了方法的幂等性;POST 不幂等,所以支付、创建这类接口要靠额外的幂等键兜底。

方法幂等?safe?典型语义
GET读取,无副作用
PUT用给定状态整体覆盖,重复结果一致
DELETE删除,重复后资源仍是不存在
POST创建 / 支付,每次都可能改变状态,需幂等键

safe 指「不产生副作用」(GET 只读);idempotent 指「重复执行的副作用等同一次」。GET 两者都是,POST 两者都不是,所以 POST 最需要幂等键保护。

什么时候需要幂等键

只要同一个意图可能因为重试而被发送多次,就需要幂等保护。

  • 支付与下单:用户连点、页面刷新或客户端超时重试,不应重复扣款或重复下单。
  • webhook 接收:发送方没及时收到 2xx 会重投;按 event_id 或业务键查重,重复事件不重复入账。
  • 队列与后台任务:消息可能被重复投递或 worker 重跑;消费侧按业务键去重,避免重复执行。
  • 任何重试路径:网络抖动、超时、客户端自动重试都可能让同一请求到达多次,副作用前都应查重。

怎么用:从生成 key 到重放结果的最短链

把一次幂等支付拆成可追查的步骤。

  1. 客户端为这一次业务意图生成唯一 Idempotency-Key(如 UUID 或「业务类型 + 订单号」),随请求发送。
  2. 服务端在执行副作用前,按 key 原子查重;命中则直接返回存储的结果,不再扣款或创建。
  3. 未命中时执行副作用,把 key、请求摘要、状态和响应一起持久化,设可解释的 TTL。
  4. 同一个 key 配不同的请求参数要拒绝并返回冲突,避免语义混淆;日志按键追查,但不记录敏感 payload。

正反例:同一个支付意图被发送两次

只差一个幂等键,余额的结果完全不同。

正例 · 带 Idempotency-Key双击支付,只扣一次
POST pay-104 → pay_001 · 扣款 ¥100POST pay-104 → 重放 pay_001 · 余额不变

同一个 key,第二次返回第一次的结果,余额 ¥300 → ¥200。重试安全。

反例 · 不带 key双击支付,扣了两次
POST → pay_001 · 扣款 ¥100POST → pay_002 · 又扣款 ¥100

POST 默认不幂等,重试和连点各执行一次,余额 ¥300 → ¥100,用户被多扣。

快速自测

支付接口收到同一个 Idempotency-Key 的第二次请求,最正确的服务端行为是?

继续查证

术语的技术定义和行为以这些一手或权威资料为准。

下一步学

和本知识点经常一起出现的概念。