连载中 11/20

分片后的事务:本地事务的边界与柔性方案

2026-07-23 · 1989 阅读 · 0 评论 · 0 赞

事务边界画在了分片线上

单库时代,事务是个幸福的问题:@Transactional 一标注,扣库存、减余额、写订单全在一个事务里,要么全成要么全无。分库之后,这个幸福的边界被物理切开了:MySQL 的事务只属于一个实例,跨实例没有天然的「要么全成」。分片系统的事务问题,本质就是重新回答——哪些写操作还能共享一个本地事务?不能共享的怎么办?

第一种:单片事务,假装没拆过

好消息是,按这个系列的设计走过来,大多数业务写天然落在单片:用户的订单、购物车、地址都用 user_id 做分片键(第 5 篇的决策),围绕一个用户的完整操作写的是同一个库,本地事务原样可用,连代码都不用改。绑定表设计(第 10 篇)又把订单与明细锁进同片。这是设计期最值得下的功夫:用分片键的规划,把事务的边界提前画对——比任何分布式事务框架都便宜。

第二种:跨片写,中间件不会替你兜底

真正要警惕的是跨片写:一笔转账要减用户 A 的余额(在分片 3)、加用户 B 的余额(在分片 7)。不带分片键感知的代码里,这类操作会写两个库——中间件默认把跨片事务拆成多个独立的本地事务顺序执行,第二个库失败时,第一个库的提交不会回滚:部分提交,账就错了一半。面对跨片写,路线有三条:XA 强一致——两阶段提交锁资源到全局事务结束,性能代价大、阻塞窗口长,分片场景用得少;柔性最终一致——本地事务加消息表,先落库再发消息补偿,短暂不一致换取高可用;业务规避——重新审视操作是否真的必须跨片,换个数据组织方式让它在单片完成。XA 那套两阶段的细节、柔性方案的完整谱系(本地消息表、TCC、Saga),是另一个大话题——后面会开一个专门的系列来讲,这里先立住结论:分片系统的第一选择永远是柔性方案,XA 是最后的保险

// 柔性路线的最小骨架:本地事务 + 消息表
@Transactional  // 单片事务:扣减与消息同库落盘
public void transferInShard(...) {
    accountDao.deduct(from, amount);      // 业务写
    msgDao.save(transferMsg, PENDING);    // 同事务记一笔待处理消息
}
// 异步任务扫 PENDING 消息,补偿对方分片,成功后标记 DONE

补偿的配套:幂等与对账

只要走上柔性路线,两件配套就不能省。幂等:补偿消息可能重复投递,加余额的操作必须天然可重——幂等键、状态机、唯一约束,总得占一样;对账:消息丢了、任务挂了,补偿链路自己也会漏,每天跑一次全局对账把漏网的差异捞出来修复。幂等与对账是柔性方案的氧气,平时没人注意,缺了立刻窒息。

一张决策卡

分片事务的决策顺序收束成四步:一查——这个业务操作能不能用分片键设计锁进单片?能就不需要任何分布式事务;二避——跨片操作能否改组织方式(冗余、异步化)减少跨片;三柔——必须跨片的走本地消息表加补偿,配齐幂等与对账;四慎——XA 只留给强一致刚需且 QPS 可控的极少数场景。顺序别反——上来就问「用什么分布式事务框架」,说明设计期欠了账。

事务讲完,系列继续往下走。下一篇转到数据本身:分片之后主键怎么办——自增 ID 在分片世界全线撞车,分布式 ID 的时代到了。

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 赞