一文搞懂分布式事务:从理论到实践方案

一文搞懂分布式事务:从理论到实践方案

在分布式系统中,一个核心操作常拆分为多个小操作,分散在不同服务器、不同应用中 ------ 比如电商下单,要同时修改订单库、库存库、支付库的数据。这就需要分布式事务来保障:这些小操作要么全部成功,要么全部失败,本质是保证不同数据库的数据一致性。

本文将从理论基础(CAP、BASE)到规范(XA、JTA),再到实践方案(2PC、3PC、TCC、Seata),逐步拆解分布式事务的核心逻辑。

一、理论基础:CAP 定理与 BASE 理论

分布式事务的设计,首先要围绕 "一致性" 和 "可用性" 的平衡展开,这就离不开 CAP 和 BASE 两大理论。

1. CAP 定理:三选二的必然选择

CAP 分别代表一致性(Consistency)可用性(Availability)分区容错性(Partition Tolerance)

  • 一致性:数据分布在多个节点,某个节点更新后,其他节点能立即读到最新数据(强一致性);若读不到,则为不一致。
  • 可用性:非故障节点需在合理时间内返回正确响应,不能无响应或报错。
  • 分区容错性:网络分区(如部分节点断连)时,系统仍能正常工作。

核心结论:分布式系统必须保证 P(分区容错性是分布式的基础),因此只能在 "CP" 和 "AP" 中选其一:

  • 选 CP(强一致性) :如 ZooKeeper、MongoDB。网络分区时,为避免数据不一致,会牺牲可用性 ------ 比如节点 A 更新后无法同步到节点 B,系统会暂停服务,不让用户读到不一致数据。
  • 选 AP(高可用性) :如 12306、电商平台。网络分区时,优先保证服务可用,允许短暂数据不一致 ------ 比如用户看到的库存是旧的,但实际下单时会再次校验,避免超卖。

2. BASE 理论:AP 的 "柔性妥协"

BASE 是对 AP 的扩展,追求基本可用(Basic Availability)软状态(Soft State)最终一致性(Eventual Consistency) ,是 CAP 中 "放弃强一致性、保可用性" 的落地指导:

  • 基本可用:允许损失部分非核心服务,保证核心功能可用 ------ 比如电商大促时,关闭 "商品评价" 功能,优先保障 "下单付款"。
  • 软状态:允许系统存在中间状态(如 "订单待支付"),不影响整体可用性。
  • 最终一致性:短期内数据可能不一致,但经过一段时间(如同步延迟),所有节点数据会趋于一致。

BASE 与 ACID(数据库事务的原子性、一致性、隔离性、持久性)相反:ACID 追求强一致性,BASE 则用 "最终一致" 换可用性,解决分布式系统的网络延迟问题。

二、核心规范:XA 与 JTA

有了理论基础,分布式事务需要具体规范来定义角色和交互逻辑,最经典的是 XA 规范和 JTA API。

1. XA 规范:定义分布式事务角色与接口

XA 是 X/Open 组织制定的分布式事务模型,核心是明确 4 个角色和交互接口:

  • Application(应用) :发起事务的服务实例(如订单服务)。
  • TM(事务管理器) :协调全局事务的组件,负责通知各节点提交 / 回滚。
  • RM(资源管理器) :数据库,负责执行本地事务(如订单库、库存库)。
  • CRM(通信资源管理器) :可选,如消息中间件,用于跨服务通信。

全局事务:XA 定义的核心概念 ------ 横跨多个 RM 的事务,需保证 "一个 RM 失败,所有 RM 都回滚"。XA 的关键是:定义 TM 与 RM 的交互接口,让 TM 能统一控制所有 RM 的提交 / 回滚,具体实现由数据库厂商提供(如 MySQL 支持 XA)。

2. JTA:Java 中的分布式事务 API

JTA(Java Transaction API)是 Java 领域对 XA 规范的实现,属于 J2EE 体系:

  • 单库事务用 JDBC 事务(如Connection.commit());
  • 跨库事务用 JTA API,通过 TM 协调多个 RM,需结合 JNDI(Java 命名服务)使用。

现状:JTA 规范虽完善,但近年在分布式微服务场景中很少使用 ------ 过于依赖 J2EE 环境,灵活性不足。

三、经典方案:从 2PC 到 3PC

基于 XA 规范,最经典的分布式事务实现是 "两阶段提交(2PC)" 和 "三阶段提交(3PC)",核心是通过 "分阶段协调" 保证一致性。

1. 两阶段提交(2PC):简单但有缺陷

2PC 是 XA 的典型实现,分 "准备阶段" 和 "提交阶段":

  • 阶段 1:准备(Prepare)

TM 向所有 RM 发送 "准备请求",RM 执行本地事务(如扣减库存),写入 Undo Log(回滚日志)和 Redo Log(重做日志),但不提交;执行成功后,RM 向 TM 返回 "就绪",否则返回 "失败"。

  • 阶段 2:提交 / 回滚(Commit/Abort)

若所有 RM 都返回 "就绪",TM 发送 "提交请求",RM 提交事务并释放锁;若有 RM 返回 "失败",TM 发送 "回滚请求",RM 通过 Undo Log 回滚。

缺陷

  • 同步阻塞:RM 等待 TM 指令时,会锁定资源,无法处理其他请求,不支持高并发;
  • 单点故障:TM 宕机后,RM 会一直卡在 "就绪" 状态,无法释放资源;
  • 脑裂问题:阶段 2 中,部分 RM 收到 "提交" 指令,部分没收到,导致数据不一致;
  • 状态丢失:TM 热备时,若某 RM 已提交但 TM 宕机,新 TM 无法获知该 RM 状态。

2. 三阶段提交(3PC):优化 2PC 的缺陷

3PC 在 2PC 基础上增加 "预准备阶段" 和 "超时机制",解决部分问题:

  • 阶段 1:预准备(CanCommit)

TM 发送 "是否可提交" 请求,RM 仅检查自身状态(如网络、资源是否可用),不执行实际事务,返回 "就绪" 或 "失败"------ 此阶段无资源锁定。

  • 阶段 2:准备(PreCommit)

若所有 RM "就绪",TM 发送 "预提交请求",RM 执行事务(写日志,不提交);若有 RM "失败",TM 发送 "中止请求",事务结束。

  • 阶段 3:提交(DoCommit)

若所有 RM "预提交成功",TM 发送 "提交请求";若有 RM "失败" 或超时,TM 发送 "回滚请求"。

优化点

  • 超时机制:RM 若超时未收到 TM 指令,会自动提交(基于 CanCommit 阶段确认的 "自身可用"),避免资源长期锁定;
  • 减少阻塞:CanCommit 阶段无资源锁定,降低同步阻塞影响。

缺陷:仍未解决脑裂问题 ------ 若阶段 3 中,部分 RM 没收到 "回滚" 指令,会自动提交,导致数据不一致。

四、实战方案:TCC、Seata 与更多

2PC/3PC 依赖数据库层,灵活性不足,实际业务中更常用 TCC、可靠消息、Seata 等方案。

1. TCC:应用层的 "两阶段提交"

TCC(Try-Confirm-Cancel)是在应用层实现分布式事务,核心是为每个业务操作定义 "准备、确认、补偿" 三个接口,不依赖数据库 XA 接口。

核心逻辑(三阶段):
  • Try:业务检查 + 资源预留 ------ 如下单时,检查库存是否足够,锁定库存(不扣减)。
  • Confirm:确认业务操作 ------ 如扣减已锁定的库存,此阶段需保证 "一定成功"(失败则重试)。
  • Cancel:补偿回滚 ------ 如释放 Try 阶段锁定的库存,恢复资源。
执行流程:
  1. TM 发起所有 RM 的 Try 操作;
  1. 若所有 Try 成功,TM 发起所有 RM 的 Confirm 操作;
  1. 若任一 Try 失败,TM 发起所有 RM 的 Cancel 操作。
关键问题与解决:
  • 幂等性:用 "事务状态表" 记录状态(INIT/CONFIRMED/ROLLBACKED),重复调用时先查状态,避免重复执行;
  • 空回滚:Cancel 时查状态表,若 Try 未执行(无 INIT 记录),则不处理;
  • 资源悬挂:Cancel 执行后,若 Try 请求延迟到达,通过状态表拦截(已存在 Cancel 记录则拒绝 Try)。
适用场景:

强隔离性、短耗时业务(如支付扣款、积分兑换),但对应用侵入性强 ------ 每个业务需写 Try/Confirm/Cancel 接口。

2. 其他常见方案

方案 核心逻辑 适用场景
可靠消息最终一致 用消息中间件异步同步数据,失败则重试 耗时业务(如订单创建后发通知)
最大努力通知 异步通知 + 有限次重试,不保证 100% 成功 非核心业务(如短信提醒)
ByteTCC 框架 封装 TCC 逻辑,自动重试 Confirm,用数据库记录事务状态 简化 TCC 开发,需建专用日志表

3. Seata:开源分布式事务框架

Seata 是阿里开源的分布式事务解决方案,支持 AT、TCC、SAGA、XA 四种模式,开箱即用,是当前微服务场景的主流选择。

核心角色:
  • TC(Transaction Coordinator) :Seata 服务端,协调全局事务;
  • TM(Transaction Manager) :应用端,发起 / 结束全局事务;
  • RM(Resource Manager) :数据库端,执行本地事务,向 TC 注册分支事务。
主流模式:AT 模式(基于 2PC 的优化)

AT 模式依赖支持 ACID 的关系型数据库,无需改业务代码,核心是 "自动生成回滚日志":

  • 一阶段:RM 执行本地事务(如扣库存),同时写入 "回滚日志"(记录数据修改前的状态),一并提交;
  • 二阶段提交:TM 通知 TC 提交,TC 异步清理回滚日志,快速结束;
  • 二阶段回滚:TC 通知 RM,RM 通过回滚日志生成 "补偿操作"(如恢复库存),完成回滚。

优势:对业务无侵入,性能优于传统 2PC,适合微服务场景。

总结

分布式事务的核心是 "平衡一致性与可用性":

  • 理论上,CAP 定调 "P 必选,CP/AP 二选一",BASE 用 "最终一致" 落地 AP;
  • 规范上,XA 定义角色接口,JTA 是 Java 实现,但灵活性不足;
  • 实践上,2PC/3PC 是基础方案,TCC 适合强一致场景,Seata(AT 模式)是微服务首选,可靠消息适合异步场景。

实际开发中,需根据业务优先级(如 "支付需强一致,通知可最终一致")选择方案,避免过度追求 "强一致" 导致可用性下降。

相关推荐
代码or搬砖5 分钟前
SpringMVC的执行流程
java·spring boot·后端
极光代码工作室1 小时前
基于SpringBoot的流浪狗管理系统的设计与实现
java·spring boot·后端
Rust语言中文社区1 小时前
【Rust日报】Dioxus 用起来有趣吗?
开发语言·后端·rust
小灰灰搞电子1 小时前
Rust Slint实现颜色选择器源码分享
开发语言·后端·rust
boolean的主人1 小时前
mac电脑安装nginx+php
后端
boolean的主人1 小时前
mac电脑安装运行多个php版本
后端
F***c3252 小时前
PHP在微服务中的分布式跟踪
分布式·微服务·php
oouy2 小时前
Java的三大特性:从懵圈到通透的实战指南
后端
狂炫冰美式3 小时前
3天,1人,从0到付费产品:AI时代个人开发者的生存指南
前端·人工智能·后端
Java水解3 小时前
PostgreSQL 自增序列SERIAL:从原理到实战
后端·postgresql