项目中的分布式事务问题
由于项目使用了微服务架构,又进行了分库分表,也有不少同学提问项目中是如何解决分布式事务的,其实项目中已经通过业务来解决了。这里我总结一下。
一、为什么不用 Seata
官网中的示例
让我们从一个微服务示例开始。
用例
用户购买商品的业务逻辑。整个业务逻辑由 3 个微服务提供支持:
- 仓储服务:对给定的商品扣除仓储数量。
- 订单服务:根据采购需求创建订单。
- 帐户服务:从用户帐户中扣除余额。
架构图
问题背景
- 多服务参与同一笔业务(如“仓储”“账户”“主业务”)使用 Seata 的 AT 模式进行分布式事务。
- 关注点:当一个全局事务正在修改“同一数据行”时,其他请求能否同时修改该行?是否必须等到当前全局事务的所有分支都结束后才能继续?
核心结论
- 在“修改同一行”的前提下,后续对该行的写会被阻塞或重试,直到当前全局事务整体结束(全局提交或回滚)后才释放锁。
- 锁是“行级全局锁”,按“表+主键”粒度生效,只影响冲突的那一行;其他不冲突的行、非强一致读不会被整体阻塞。
AT 模式如何工作
- 两阶段事务:
- 一阶段(本地执行/提交):每个分支在自己的本地事务中完成写入,同时记录 undo_log 并为所改行加“全局锁”( LockKey = 表名 + 主键 )。
- 二阶段(全局提交/回滚):全局提交时释放锁;全局回滚时用 undo_log 恢复数据并释放锁。
- 锁释放时机:发生在“全局事务结束”(提交或回滚),不是单个分支本地提交的时刻。
行为示例
场景:全局事务 xid1 包含分支 a1/b1/c1,三者都要修改同一行(例如库存表主键 S1 )。
- 当 a1 已提交本地事务、b1 仍在执行、c1 已提交本地事务时,后续请求 a2 若要写同一行 S1 :
- 会检测到该行的全局锁仍被 xid1 持有;
- a2 的写要么等待、要么重试、要么失败(取决于客户端重试配置);
- 只有当 xid1 整体“全局提交/回滚”后,锁释放,a2 才能获取锁并写入。
对比不冲突情况
- a2 写不同主键(例如 S2 )→ 可并发执行,不受 S1 的锁影响。
- 普通查询(非强一致读)→ 不受全局锁影响。
会阻塞与不会阻塞
- 会阻塞:
- 对同一 LockKey (同一表同一主键)的写操作。
- 强一致读( SELECT ... FOR UPDATE 或使用 @GlobalLock )命中同一锁时。
- 不会阻塞:
- 写不同主键的行、写不同表的记录。
- 普通读操作(非强一致),不会参与全局锁。
付费内容提示
该文档的全部内容仅对「JavaUp项目实战&技术讲解」知识星球用户开放
加入星球后,你可以获得:
- 超级八股文:100万+字的全栈技术知识库,涵盖技术核心、数据库、中间件、分布式等深度剖析的讲解
- 讲解文档:黑马点评Plus、大麦、大麦pro、大麦AI、流量切换、数据中台的从0到1的550+详细文档
- 讲解视频:黑马点评Plus、大麦、大麦pro、大麦AI、流量切换、数据中台的核心业务详细讲解
- 1 对 1 解答:可以对我进行1对1的问题提问,而不仅仅只限于项目
- 针对性服务:有没理解的地方,文档或者视频还没有讲到可以提出,本人会补充
- 面试与简历指导:提供面试回答技巧,项目怎样写才能在简历中具有独特的亮点
- 中间件环境:对于项目中需要使用的中间件,可直接替换成我提供的云环境
- 面试后复盘:小伙伴去面试后,如果哪里被面试官问住了,可以再找我解答
- 远程的解决:如果在启动项目遇到问题,本人可以帮你远程解决
进入星球后,即可享受上述所有服务,保证不会再有其他隐藏费用。
