news 2026/10/3 12:43:42

System Design 101 幂等性实战:6 大典型应用场景与工程实现要点

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
System Design 101 幂等性实战:6 大典型应用场景与工程实现要点
  • 后端
  • 文档
  • 教程

【免费下载链接】system-design-101

Explain complex systems using visuals and simple terms. Help you prepare for system design interviews.

项目地址:https://gitcode.com/GitHub_Trending/sy/system-design-101
点击查看免费下载

本文是开源仓库 System Design 101 中「Top 6 Cases to Apply Idempotency」指南的深度展开版,围绕"操作可能被重试、可能被多次执行"这一前提,逐一拆解 RESTful API、支付、订单、数据库、账号管理与分布式消息六大场景中的幂等性诉求,并结合仓库内 如何避免双重扣款、失败重试策略、消息投递语义 等配套文档,给出可直接落地的幂等键、重试退避、去重与补偿方案。读完本文,你将能够判断一个操作"是否必须幂等"、选择正确的幂等实现层次,并为一套面向生产环境的可靠系统设计出"重试安全、扣款不重、下单不重、消息不重处理"的完整防线。

什么是幂等性:先理解"一次成功、多次无害"

幂等性(Idempotency)是指:同一个操作被执行一次与被执行多次,最终产生的状态是一致的。它尤其重要于那些"可能被重试、可能被多次执行"的场景——网络超时后的客户端重试、消息队列的重复投递、支付网关的回调重放,都会让一个操作在物理上执行不止一次。

从数学上讲,一个操作是幂等的,当且仅当f(f(x)) = f(x):第一次执行改变了状态,第二次及以后的执行不再产生新的变化。

仓库文档 如何避免双重扣款 给出了一个更工程化的拆解方式:"恰好一次(exactly-once)" = "至少一次(at-least once)" + "至多一次(at-most once)"。这两半分别由两种机制保证:

  • 重试(Retry):提供"至少一次"保证——因为网络可能随时失败,客户端必须敢于重发,确保操作最终被送达;
  • 幂等性检查(Idempotency Check):提供"至多一次"保证——服务端收到重复请求时能识别并丢弃,确保操作只生效一次。

配套的 消息投递语义 对这三档语义做了进一步界定:

投递语义含义典型适用场景
At-most once消息最多送达一次,可能丢失但不重复监控指标等可容忍少量丢失的场景
At-least once消息不丢失,但可能被重复投递数据重复影响不大、或消费端可去重的场景
Exactly once既不丢失也不重复,实现成本最高支付、交易、记账等金融场景,且下游不支持幂等时

幂等性正是"在 at-least once 的基础上,用应用层去重把重复执行挡在门外",从而让系统整体呈现出 exactly-once 的行为。

幂等键(Idempotency Key):幂等实现的通用载体

在客户端与服务端之间,幂等性通常通过幂等键落地。依据 如何避免双重扣款 的说明:

  • 幂等键是由客户端生成的唯一值,且在一定时间后过期;
  • UUID 是最常用的幂等键,并被 Stripe、PayPal 等多家公司推荐使用;
  • 发起幂等请求时,把幂等键放进 HTTP 请求头:<idempotency-key: key_value>。

服务端收到请求后,先以幂等键查询是否已处理过:若未处理则执行并记录;若已处理则直接返回首次执行的结果(或确认状态),不再重复执行。

场景一:RESTful API 请求——重试不改变资源状态

核心诉求:确保重试一次 API 请求不会导致同一操作被执行多次,从而维持资源状态的一致。

实现要点:

  1. 选择幂等的 HTTP 方法。REST 语义中,GET用于读取、PUT用于整体替换资源、DELETE用于删除资源,这些方法天然是幂等的:同一请求重发任意多次,资源最终状态不变。POST用于创建,通常不幂等——这正是需要幂等键保护的典型对象。
  2. 为不幂等的操作绑定幂等键。对POST这类创建型请求,客户端在请求头携带idempotency-key,服务端按键去重,即可把"不幂等的 POST"变成"业务上幂等的 POST"。
  3. 保持幂等方法的语义纯粹。PUT必须做到"全量替换"而不是"追加修改",否则第二次重放会基于已变化的状态产生错误结果;DELETE重复执行应返回"已删除/不存在"的一致结果而非报错。

场景二:支付处理——绝不重复扣款

核心诉求:网络抖动、网关超时、回调重放都不能让用户被扣两次钱。支付网关本身就经常需要重试交易,幂等性保证"一笔订单只产生一次扣款"。

这是整个分布式系统中幂等性最关键的战场。结合 如何避免双重扣款,支付侧的标准做法是:

  1. 重试保证"至少一次"。客户端因网络差、超时而重试支付请求(例如图中第四次的尝试才成功),请求可能被多次送到服务端。
  2. 幂等键保证"至多一次"。每次支付请求都携带客户端生成的 UUID 幂等键(<idempotency-key: key_value>),支付服务按键落库并去重,无论请求到达几次,扣款动作只执行一次。
  3. 重试要配合退避策略,而不是无脑风暴式重试。仓库文档 失败重试策略 归纳了四种常见策略:
策略行为优点缺点
Linear Backoff(线性退避)每次等待固定递增间隔实现简单、易理解高并发下易引发资源争用与"重试风暴"
Linear Jitter Backoff(线性抖动退避)线性间隔外加随机抖动随机性打散各实例的重试时间,降低同步重试概率基础间隔线性增长,仍可能触发同步重试
Exponential Backoff(指数退避)间隔按 1s、2s、4s、8s……指数增长,通常设上限显著降低系统负载与重试碰撞概率,适合高负载本可快速重试解决的场景反而被拖慢
Exponential Jitter Backoff(指数抖动退避)指数间隔叠加随机抖动(加性/乘性)兼具指数退避优点,进一步降低重试碰撞抖动较大时可能产生过长等待

支付场景应优先采用带抖动的指数退避,既保证重试最终成功,又避免同时重试打垮下游。

支付链路本身也要在每一环防重。参照 支付系统 的典型流程:用户点击购买后生成支付事件并入库;一个支付事件可能包含多个支付订单(如一个购物车内多家卖家的商品);支付执行器逐个调用外部 PSP 完成扣款,成功后更新钱包、追加账本(Ledger),夜间再由 PSP/银行下发对账文件。这条链路上,事件、订单、扣款、钱包入账、账本追加都需要各自的幂等保护,否则任何一个环节的重放都会造成金额不一致。

此外,支付对账 明确指出:即便系统实现了 exactly-once 语义,仍可能存在各种意外差异,对账系统是必须的安全网——通过比较电商订单、支付渠道交易记录、账本借贷记录,发现并消除不一致。幂等性负责"事前不重",对账负责"事后兜底"。

场景三:订单管理系统——下单多次只成一单

核心诉求:用户(或客户端重试、前端重复点击)多次提交同一订单,系统只创建一个订单,并防止库存被重复扣减。

实现要点:

  1. 客户端生成订单幂等键。下单请求携带idempotency-key(UUID),服务端先查幂等表:键已存在则直接返回原订单,否则创建新订单并记录键。
  2. 库存扣减必须幂等或有唯一约束兜底。库存流水表以"订单 ID + 商品 ID"作为唯一约束(详见下文场景四),重复的扣减请求被数据库直接拒绝;或让"扣减"以订单维度做去重,保证同一订单只扣一次。
  3. 超时重试与下单去重配合。下单请求超时后客户端重试是常态,幂等键让"第二次请求"命中第一次的结果,从源头杜绝重复下单、重复扣库存。

场景四:数据库操作——事务重放不改变最终状态

核心诉求:一条事务/一条 SQL 被重新执行(重放)时,数据库状态不应发生超出首次执行的改变。

实现要点:

  1. 善用"原子性幂等写入"。将"先查后写"改为"条件写入/冲突覆盖":INSERT ... ON CONFLICT DO NOTHING / DO UPDATE(UPSERT)保证同一行数据只被初始化一次;INSERT IGNORE等数据库方言同理。重复执行对已有数据无影响。
  2. 用唯一约束做最终防线。这是最硬核的幂等保障。消息投递语义 指出:在 at-least once 语义下,给每条消息(或业务记录)一个唯一键,写入数据库时遇到重复直接拒绝——唯一索引将"重复写入"变成"必然失败"的确定性结果。
  3. 注意与事务隔离级别的配合。幂等写入在高并发下要防止两个请求同时"都判定未存在、都去写入",唯一约束与适当的事务隔离级别(可参考仓库 数据库隔离级别)能共同保证并发下仍只有一个生效。

场景五:用户账号管理——注册不重复、重置只一次

核心诉求:重试注册请求不会创建多个用户账号;多次密码重置请求最终只产生一次重置动作。

实现要点:

  1. 注册场景用"唯一标识 + 幂等键"双保险。邮箱、手机号、用户名上加唯一索引,是数据库层面"不可重复注册"的硬约束;客户端携带的idempotency-key则保证同一次注册请求即使被重试 N 次,也只走一遍注册流程、只发一封验证邮件。
  2. 密码重置场景"以重置令牌为核心"。重置请求生成一次性令牌(token),令牌使用即失效——后续任何重复的重置请求要么命中同一个令牌、要么因令牌已失效被拒绝,从而保证"多次请求、一次重置"。同时可对同一账号的重复重置请求按幂等键去重,避免短信/邮件验证码被重复下发。
  3. 写操作配合唯一键落库。账号表、重置令牌表均可通过唯一约束承接重复写入(同场景四),让数据库成为幂等性的最后一层保障。

场景六:分布式系统与消息——队列重放不产生重复处理

核心诉求:消息队列中的同一条消息被重新投递/重新处理时,不应造成重复处理或重复副作用。需要实现能"多次处理同一条消息而不产生副作用"的处理器。

这是分布式系统幂等性的核心应用,可以分成三层来看:

  1. 投递侧:理解 at-least once 是常态。以 Kafka 为例,仓库文档 Kafka 会丢消息吗 指出:生产者侧需要配置合理的acks与retries才能确保消息送达;消费者侧不同的提交(commit)方式会影响语义——自动提交可能在消息真正处理完之前就提交了 offset,一旦消费者在中间宕机,部分消息会"未被处理但已提交",或反过来被重新消费。因此消费端必须假定"同一条消息可能被处理多次"。
  2. 消费侧:处理函数必须幂等。最直接的做法是在消费端去重:给每条消息携带唯一键(业务 ID 或消息 ID),消费时先查重(如通过唯一索引写入、幂等表、或 布隆过滤器 这类空间高效的概率结构做大规模去重)再执行业务逻辑。布隆过滤器可回答"URL/消息是否已存在",false negative 不会出现,false positive 可用唯一索引二次兜底。
  3. 状态侧:副作用要可重放。更新类消费(写库、扣库存、发通知)都要按场景一至四的方式做成幂等写,或采用"本地事务表 + 消息表"(如 Outbox 模式)保证消息与业务状态同时落库,避免"状态变了但消息丢了"或"消息重了但状态没跟着重"的错位。

小结:六大场景的幂等方案速查

场景风险点推荐方案
RESTful API 请求POST 重试导致重复创建/变更用 PUT/DELETE 等幂等方法;POST 绑定幂等键
支付处理重试导致重复扣款幂等键 + 指数抖动退避 + 对账兜底
订单管理重复提交产生重复订单、重复扣库存订单幂等键 + 库存流水唯一约束
数据库操作事务重放改变状态UPSERT、唯一索引、唯一键去重
用户账号管理重复注册、重复重置邮箱/手机号唯一约束 + 一次性令牌 + 幂等键
分布式消息队列重放导致重复处理消息唯一键 + 消费端去重(唯一索引/幂等表/布隆过滤器)+ 幂等处理器

幂等性不是一个独立的功能模块,而是一套贯穿"客户端重试、网关透传、服务端去重、数据库约束、消息消费"的设计纪律。把"至少一次"的重试与"至多一次"的幂等检查组合起来,你的系统就能在不确定的网络世界里给出确定的业务结果——这正是 System Design 101 仓库想传达的核心理念:用简单清晰的工程手段,构建可靠的大规模系统。

本文的六类场景与配套方案均可回到仓库对应文档继续深挖:如何避免双重扣款(幂等键与 exactly-once 拆解)、失败重试策略(四种退避算法)、消息投递语义(三档语义与去重)、支付系统(支付链路各环节)、支付对账(对账安全网)、Kafka 会丢消息吗(消息生命周期与提交策略)、布隆过滤器去重(大规模去重)。

  • 后端
  • 文档
  • 教程

【免费下载链接】system-design-101

Explain complex systems using visuals and simple terms. Help you prepare for system design interviews.

项目地址:https://gitcode.com/GitHub_Trending/sy/system-design-101
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 12:43:39

LeetCode Hot100 矩阵专题题解笔记

LeetCode Hot100 矩阵专题题解 73. 矩阵置零 题目 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用原地算法。 思路 定义两个布尔数组&#xff0c;分别标记哪些行、哪些列存在0元素第一次遍历矩阵&#xff0c;记录…

作者头像 李华
网站建设 2026/10/3 12:42:30

外贸获客AI具体能做什么?2026年主流方案对比

用简单直接的话来说, 外贸获客AI做的事情就是把“寻找客户、筛选客户、撰写开发信、跟进转化”这一整套工作流程交给系统去自动执行。在过去, 一名销售员需要花费大量的时间和精力, 一天之内才能发送30到50封开发信。 而如今, AI已经将目标客户的电子邮箱、公司的详细背景信息以…

作者头像 李华
网站建设 2026/10/3 12:39:55

坏人识别地图

坏男人行径有哪些&#xff1f; PUA、高姿态不尊重、服从性测试、脚踏几只船、中央空调对谁都暖、不负责任。除了这些还有哪些&#xff1f;给我全面的回答&#xff0c;尽可能搜集​一下再根据这些每一个类型&#xff0c;都给我在后面加个双引号&#xff0c;然后加一句这个类型渣…

作者头像 李华
网站建设 2026/10/3 12:39:03

redis--集群

一、分片集群的出现背景主从 哨兵只能解决高可用、读高并发&#xff0c;无法解决两个痛点&#xff1a;海量数据存储&#xff08;单 master 容量上限&#xff09;写高并发&#xff08;写请求只能打在 master&#xff0c;写压力无法水平扩展&#xff09;分片集群&#xff08;Red…

作者头像 李华
网站建设 2026/10/3 12:38:43

企业自媒体账号没人更新怎么办

企业自媒体账号没人更新怎么办&#xff1f; 不少公司都注册过公众号或者抖音号&#xff1a;注册那天拍过照、发过两三篇&#xff0c;然后一件事接一件事&#xff0c;账号就没人管了。几个月后想起来&#xff0c;最后一条更新还停在半年前。这时候摆在面前的就三个选项&#xff…

作者头像 李华