『』分布式事务如何实现?深入解读 Seata 的 XA 模式( 三 )


不存在某一种分布式事务机制可以完美适应所有场景 , 满足所有需求 。
XA 规范早在上世纪 90 年代初就被提出 , 用以解决分布式事务处理这个领域的问题 。
现在 , 无论 AT 模式、TCC 模式还是 Saga 模式 , 这些模式的提出 , 本质上都源自 XA 规范对某些场景需求的无法满足 。
XA 规范定义的分布式事务处理机制存在一些被广泛质疑的问题 , 针对这些问题 , 我们是如何思考的呢?
1. 数据锁定:数据在整个事务处理过程结束前 , 都被锁定 , 读写都按隔离级别的定义约束起来 。
思考:
数据锁定是获得更高隔离性和全局一致性所要付出的代价 。
补偿型 的事务处理机制 , 在 执行阶段 即完成分支(本地)事务的提交 , (资源层面)不锁定数据 。 而这是以牺牲 隔离性 为代价的 。
另外 , AT 模式使用全局锁保障基本的写隔离 , 实际上也是锁定数据的 , 只不过锁在 TC 侧集中管理 , 解锁效率高且没有阻塞的问题 。
2. 协议阻塞:XA prepare 后 , 分支事务进入阻塞阶段 , 收到 XA commit 或 XA rollback 前必须阻塞等待 。
思考:
协议的阻塞机制本身并不是问题 , 关键问题在于 协议阻塞 遇上 数据锁定 。
如果一个参与全局事务的资源 “失联” 了(收不到分支事务结束的命令) , 那么它锁定的数据 , 将一直被锁定 。 进而 , 甚至可能因此产生死锁 。
这是 XA 协议的核心痛点 , 也是 Seata 引入 XA 模式要重点解决的问题 。
基本思路是两个方面:避免 “失联” 和 增加 “自解锁” 机制 。 (这里涉及非常多技术细节 , 暂时不展开 , 在后续 XA 模式演进过程中 , 会专门拿出来讨论)
3. 性能差:性能的损耗主要来自两个方面:一方面 , 事务协调过程 , 增加单个事务的 RT;另一方面 , 并发事务数据的锁冲突 , 降低吞吐 。
思考:
和不使用分布式事务支持的运行场景比较 , 性能肯定是下降的 , 这点毫无疑问 。
本质上 , 事务(无论是本地事务还是分布式事务)机制就是拿部分 性能的牺牲, 换来 编程模型的简单。
与同为 业务无侵入 的 AT 模式比较:
首先 , 因为同样运行在 Seata 定义的分布式事务框架下 , XA 模式并没有产生更多事务协调的通信开销 。
其次 , 并发事务间 , 如果数据存在热点 , 产生锁冲突 , 这种情况 , 在 AT 模式(默认使用全局锁)下同样存在的 。
所以 , 在影响性能的两个主要方面 , XA 模式并不比 AT 模式有非常明显的劣势 。
AT 模式性能优势主要在于:集中管理全局数据锁 , 锁的释放不需要 RM 参与 , 释放锁非常快;另外 , 全局提交的事务 , 完成阶段 异步化 。
3. XA 模式如何实现以及怎样用?
3.1 XA 模式的设计
3.1.1 设计目标
XA 模式的基本设计目标 , 两个主要方面:

  1. 从 场景 上 , 满足 全局一致性 的需求 。
  2. 从 应用上 , 保持与 AT 模式一致的无侵入 。
  3. 从 机制 上 , 适应分布式微服务架构的特点 。
整体思路:
  1. 与 AT 模式相同的:以应用程序中 本地事务 的粒度 , 构建到 XA 模式的 分支事务 。
  2. 通过数据源代理 , 在应用程序本地事务范围外 , 在框架层面包装 XA 协议的交互机制 , 把 XA 编程模型 透明化 。
  3. 把 XA 的 2PC 拆开 , 在分支事务 执行阶段 的末尾就进行 XA prepare , 把 XA 协议完美融合到 Seata 的事务框架 , 减少一轮 RPC 交互 。
3.1.2 核心设计
1. 整体运行机制
XA 模式 运行在 Seata 定义的事务框架内:
『』分布式事务如何实现?深入解读 Seata 的 XA 模式
本文插图