连载中 10/18

幂等设计:分布式事务的压舱石

2026-06-18 · 1489 阅读 · 0 评论 · 0 赞

每篇都出现的两个字

回头数一数前九篇:本地消息表要「消费幂等」,事务消息要「消费幂等」,TCC 的 Confirm/Cancel 要幂等,Saga 补偿要幂等,最大努力通知要防重复处理——幂等是分布式事务的地基,地基不牢,上面盖什么都塌。为什么重复如此普遍?MQ 至少一次投递、框架超时重试、用户双击提交、网关重发请求——在分布式世界,「只执行一次」是要花大价钱构造的幻觉,「至少一次 + 幂等消费」才是便宜又可靠的组合。

先分清天然幂等与天然不幂等

幂等的定义不绕弯:同一个操作执行一次和执行多次,效果相同。按这个标准给写操作分两类:天然幂等——把状态设为固定值(status 改成 PAID)、按 ID 删除、把余额更新为指定值;天然不幂等——insert 一条新记录(重复插两行)、相对值变更(余额减 100,执行两遍减 200)、发送一条新消息。设计幂等的功夫,就是把不幂等的写法改造成幂等的写法,五大武器各有战场。

武器一:唯一索引,最后的墙

给业务唯一键(订单号、支付流水号)建唯一索引,重复插入直接报错——这是数据库层面最后的防线。它的珍贵之处在于原子性:应用层的一切「先查后写」都有竞态窗口(两个请求同时查「没处理过」,然后都去处理),唯一索引的插入冲突是数据库在锁层面保证的,没有竞态。凡是能找到业务唯一键的场景,无脑上唯一索引。

武器二:去重表,消费端标配

没有天然唯一键的操作(比如「给用户加积分」),建一张去重表,以消息 ID 或业务键为主键:消费前先插去重记录,插入成功才执行业务,插入冲突说明处理过,直接幂等返回。进阶版把去重记录和业务变更放进同一个本地事务——业务做了,去重记录在;业务没做,去重记录也不在,天然避免「标记了没做」的半提交。

武器三:状态机,防乱序防重复一箭双雕

业务对象的一生是状态的流转:待支付 → 已支付 → 已发货 → 已完成,流转规则写死:只有当前状态匹配才允许流转。update orders set status=PAID where id=? and status=PENDING——重复的支付回调第二次执行时状态已不是 PENDING,影响行数为零,天然被拒。状态机还能防乱序(发货请求先于支付到达时被拒绝),是订单类业务的首选骨架。

武器四:Token 机制,防用户手抖

前端防重复提交的标准解:进入下单页先领一个一次性 token,提交订单时带上;服务端用 Redis 的原子删除(DEL 返回 1 才是抢到)校验——删成功说明是第一次提交,执行业务;删不到说明 token 已被用过,拒绝。适合没有业务唯一键的场景(用户可能真的要下两单,只能靠 token 区分「这一次提交」和「上一次提交」)。

武器五:乐观锁版本号

update account set balance=?, version=version+1 where id=? and version=?——带版本号的更新天然幂等(同一版本只能成功一次),顺带解决并发丢失更新。适合状态流转不明显的资源类数据(配置、库存快照),代价是冲突时需要重试逻辑。

选型一张表

武器适用局限
唯一索引有业务唯一键的插入要求键可定义
去重表MQ 消费、回调处理多一张表的维护
状态机订单等有生命周期的对象无状态对象用不上
Token前端防重复提交依赖 Redis 可用性
乐观锁资源类并发更新冲突需重试

两条纪律

一:幂等键要用业务语义键。用「支付单号」做幂等键是对的;用随机生成的请求 ID,重试时生成个新的,幂等形同虚设。幂等键的选取和第 12 篇(分布式 ID)同理——它是另一个「十年合约」级别的决策。

二:别用先查后写冒充幂等。先 select 判断再 insert/update,两个并发请求都能通过检查——竞态窗口里没有幂等。检查逻辑可以有,但兜底必须是原子的:唯一索引、原子命令、带条件的 update。

小结

幂等篇一句话:重复是分布式的常态,五大武器按场景选择;唯一索引是最后的墙,业务语义键是幂等的魂,先查后写不是幂等。手握幂等这块压舱石,前面所有方案才算真正闭环。接下来进入框架篇——Seata 四模式一张图选型,看老牌开源框架怎么把前九篇的方案工程化。

503

10 年全栈工程师 · 503咖啡馆主理人

#分布式事务#幂等#唯一索引#去重表#状态机

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞