J004-分布式事务解决方案

status
Published
slug
J004
type
Post
category
Technology
date
Feb 20, 2025
tags
笔记
J计划
分布式
summary
分布式事务,说实话之前准备秋招的时候有简单看过,但是因为没有真正实践过,所以难得面试遇到这个问题,我虽然知道一些概念也只能说「不会」,有时间正好读到这篇文章,补齐一下这部分的知识点。除了文章的内容,还结合之前的笔记做了一些知识点的补充
💡
分布式事务,说实话之前准备秋招的时候有简单看过,但是因为没有真正实践过,所以难得面试遇到这个问题,我虽然知道一些概念也只能说「不会」,有时间正好读到这篇文章,补齐一下这部分的知识点。除了文章的内容,还结合之前的笔记做了一些知识点的补充

什么是事务?

  • 定义:一组不可分割的操作集合

事务的ACID特性

  • Atomicity(原子性):事务是不可再分的原子操作,原子性是指一个事务是一个不可分割的工作单位,事务中的所有操作要么都成功执行,要么都取消执行
  • Consistency(一致性):指事务对数据库的每一个插入、删除、修改等更新操作,都必须遵守一定的完整性约束,避免操作对数据正确性的影响
    • 一致性是目的,数据库通过原子性、隔离性、持久性来保证一致性
    • 举例:A账户100,B账户100,转账N次,两个账户的资金总额应为200
  • Isolation(隔离性):并发事务之间的相互影响程度,隔离性也是分级别的:读未提交、读已提交、可重复读等。
    • 指两个或多个事务可以同时运行而不相互影响,一个事务内部的操作及其使用的数据对并发的其他事务是隔离的、不可见的
      • 具体情况视隔离级别决定,如读未提交,在事务中可以查看其他事务还没有提交的修改
    • 无法保证隔离性会出现数据不一致的情形
  • Durability(持久性):指一个事务成功完成之后,其工作的结果就会永远保存在数据库中,是永久有效的,即使随后系统发生故障也能保持或恢复。

事务的类型划分

💡
单体数据库事务很容易满足事务的ACID特性,提供强一致性保证,但是分布式事务要完全遵循ACID特性会比较困难。
  • 为了追求分布式系统的高可用和高吞吐,分布式事务的解决方案一般提供的是最终一致性
  • 刚性事务:提供强一致性的事务
    • 完全满足ACID四个特性
    • 刚性事务追求强一致性,所以牺牲了高可用性
  • 柔性事务:提供最终一致性的事务
    • 分布式事务大多属于柔性事务,如下文中的TCC、Saga
    • 柔性事务通过牺牲一致性换来了系统的高可用性。
    • 对ACID的支持:
      • 原子性:完全支持。
      • 一致性:只提供最终一致性支持。
      • 隔离性:不完全保证,通常为了系统的吞吐和性能,会一定程度上放弃对隔离性的要求。
      • 持久性:完全支持。
  • CAP理论
    • 指在分布式系统中,如下的三个特性无法同时满足,最多只能实现其中的两项。(不可能三角)
    • Consistency(一致性)
    • Availability(可用性)
    • Partition Tolerance(分区容错性)
  • BASE理论:是 CAP 中保证分区容错(P)的前提下,对可用性(A)和一致性(C)的权衡,柔性事务一般遵循该理论
    • 当今互联网分布式系统的实践总结,在各应用提供高可用服务的基础上,尽最大能力保证数据的最终一致性
    • 由三部分组成:
      • BA:Basic Availability,基本业务可用性。
      • S:Soft state,柔性状态。
      • E:Eventual consistency,最终一致性。

分布式事务

单体应用/数据库无法满足性能/业务需求时,企业会对服务进行改造,目前流行的微服务架构和跨服务的事务场景都需要分布式事务,去确保数据的一致性
  • 定义:分布式事务是指是指事务的发起者、参与者、数据资源服务器以及事务管理器分别位于分布式系统的不同节点之上。
  • 分布式事务的三种场景
    • 跨数据库分布式事务
    • 跨服务分布式事务
    • 混合式分布式事务
  • 面临的问题
    • 分布式事务中涉及的参与者分布在异步网络中,参与者通过网络通信来达到分布式一致性,网络通信不可避免出现失败、超时的情况,因此分布式事务的实现比本地事务面临更多的困难。
下面介绍几种常见的分布式事务解决方案。

分布式事务模式

XA Specification

标准化组织 X/Open基于Tuxedo推出的分布式事务规范
  • 分布式事务处理模型的核心角色
    • RM(Resource Managers):资源管理器,提供数据资源的操作、管理接口,保证数据的一致性和完整性。
      • 最有代表性的就是数据库管理系统,文件系统、MQ 系统也可以看作 RM。
      • XA 规范中分布式事务是构建在 RM 本地事务(即分支事务)的基础上
    • TM(Transaction Managers):事务管理器,是一个协调者的角色,协调跨库事务关联的所有 RM 的行为。
      • TM 负责协调分支事务要么都成功提交、要么都回滚
    • AP(Application Program):应用程序,按照业务规则调用 RM 接口来完成对业务模型数据的变更,当数据的变更涉及多个 RM 且要保证事务时,AP 就会通过 TM 来定义事务的边界,TM 负责协调参与事务的各个 RM 一同完成一个全局事务。
    • CRMs(Communication Resource Managers):主要用来进行跨服务的事务的传播。
  • 相关定义:
    • 事务发起者/root节点:发起分布式事务的TM实例
      • 负责开启整个全局事务
    • 事务参与者:发起者之外参与分布式事务的TM实例
      • 各自负责执行自己的事务分支
    • Superior:特殊的事务发起者,若TM实例发起了对其他TM实例的服务调用时,发起者就被称为 Superior
    • Subordinate:被调用者就被称之为 Subordinate 节点。
  • 两阶段提交协议
    • XA规范中分布式事务处理的过程
    • 原理:两阶段提交把单个事务的提交拆分成了 2 个阶段,分别是「预备阶段」和「提交/回滚阶段
        1. 预备阶段
          1. TM记录事务开始日志,并询问各个 RM 是否可以执行提交准备操作。
          2. RM收到TM的指令后,评估自己的状态,尝试执行本地事务的预备操作
              • 具体操作内容:预备资源,为资源加锁、执行操作等
                • 并不提交事务,等待 TM 的后续指令
              • 若操作失败:告知 TM 本阶段执行失败并且回滚自己的操作,然后不再参与本次事务
          3. 若TM 收到所有 RM 的响应,则记录事务准备完成日志
        1. 提交/回滚阶段
          1. 根据上个阶段的协调结果发起事务的提交或者回滚操作
            • 若上一阶段所有RM都返回执行成功
              • TM记录事务Commit日志,向所有RM发起事务提交指令
              • RM收到指令后,提交事务,释放资源,并向TM响应“提交完成”
              • 如果TM收到所有RM的响应,记录事务结束日志
            • 若上一个阶段有RM返回执行失败或超时没有应答,TM都按执行失败处理
              • 记录事务abort日志,向所有RM发送事务回滚指令
            • RM收指令后,回滚事务,释放资源,并向TM响应回滚完成
            • 若TM收到所有RM的响应,则记录事务结束日志
      notion image
    • XA规范中的优化措施:
      • 退化为一阶段提交:如果 TM 发现整个事务只涉及到一个 RM,那么就会将整个过程退化为一阶段提交。
      • 只读操作:如果 RM 收到的 AP 的数据操作是只读操作,那么它可以在阶段 1 就将事务完成并告知 TM 其不再参与阶段 2 的过程。
        • 会有脏读的风险。
      • Heuristic Completion:如果 RM 在阶段1完成后,长时间等不到阶段 2 的指令,那么其可以自动提交或者回滚本地事务。
        • 注意这种场景有可能会破坏事务的一致性,产生异常。
      XA规范中的异常处理:(不同XA规范的实现,对异常处理的做法可能不同)
      • TM 在阶段 1 中询问 RM 前宕机,恢复后无需做任何操作。
      • TM 在阶段 1 中询问 RM 后宕机,可能只有部分 RM 收到了阶段 1 的请求,因此此时需要向 RM 发起回滚请求。
      • TM 在阶段 1 中询问 RM 完毕,但是在就准备完成日志时宕机,因不清楚宕机前的事务协商的结果,因此恢复后需要向 RM 发起回滚请求。
      • TM 在阶段 1 中记录完毕事务准备完成日志后宕机,恢复后可以根据日志发起提交或者回滚的指令。
      • TM 在阶段 2 中记录 commit/abort 日志前宕机,恢复后可以根据日志发起提交或者回滚指令。
      • TM 在阶段 2 中记录事务结束日志前宕机,恢复后可以根据日志发起提交或者回滚指令。
      • TM 在阶段 2 中记录事务结束日志后宕机,恢复后无需做任何操作。
      • 阶段 1 中,RM 有超时情况时,TM 按失败处理,给所有 RM 发送回滚指令。
      • 阶段 2 中,RM 有超时情况是,TM 需要对超时的 RM 持续重复发送指令。
  • XA两阶段提交协议中的ACID
    • 原子性:在prepare和commit阶段保证事务是原子性的
    • 一致性:XA协议实现的是强一致性
    • 隔离性:XA事务在完成之前一直持有资源的锁,所以可以做到写隔离
    • 持久性:基于本地事务实现
  • XA协议的应用
    • 最早出现的分布式事务规范,主流数据库MySQL、Oracle、SQLServer等都支持XA规范,J2EE中的JTA规范也是参照 XA 规范编写的,与 XA 规范兼容
  • XA协议的优点:
    • 在资源管理层面实现的分布式事务模型,对业务的入侵性较低
    • 可以覆盖分布式事务的三种场景
  • XA协议的缺点:
    • 死锁风险:由于在全局事务的执行过程中,RM一直持有资源的锁,当参与的RM过多,尤其是跨服务的场景下,网络通信的次数和时间会急剧变多,阻塞的时间更长,系统的吞吐能力变得很差,死锁出现的概率较大
      • 并不适合微服务架构中跨服务的分布式事务模式
    • 单点故障的风险:每一个TM域中,由于TM是单点,存在单点故障风险
      • 若TM在阶段一之后挂掉,会导致参与的 RM 长时间收不到阶段 2 的请求而长期持有资源的锁,影响业务的吞吐能力
    • 交互繁琐:一次完整的全局事务,TM 和 RM 之间的交互多达 8 次,太繁琐,会影响系统的处理性能
    • 脑裂风险:TM 在阶段 2 通知 RM 提交事务时,如果指令发出后就宕机了,而只有部分 RM 收到了提交请求,那么当 TM 恢复的时候,就无法协调本次事务所有的 RM 本地事务的一致性了。
  • 两阶段提交的改进:
    • 三阶段提交方案主要解决了单点故障问题,并在 RM 侧也引入了超时机制,以避免资源的长时间锁定
      • 无法避免脑裂异常
      • 实际应用案例很少

TCC

TCC (Try、Commit、Cancel) 是一种补偿型事务,该模型要求应用的每个服务提供 try、confirm、cancel 三个接口
  • 核心思想:通过对资源的预留(提供中间态),尽早释放对资源的加锁,如果事务可以提交,则完成对预留资源的确认,如果事务要回滚,则释放预留的资源。
    • 本质也是一种两阶段提交协议,但不会长时间持有资源锁
    • 将分布式事务从资源层提到业务层来实现,可以让业务灵活选择资源的锁定粒度,并且全局事务执行过程中不会一直持有锁,所以系统的吞吐量比 2PC/XA 模式要高很多。
  • TCC模型将事务的提交划分为两个阶段
    • 阶段1(try):完成业务检查(一致性),预留业务资源(准隔离性)
    • 阶段2(confirm/cancel):如果try阶段所有业务资源都预留成功,则执行confirm操作,否则执行cancel操作
      • confirm:不做任何业务检查,仅使用预留的资源执行业务操作,如果失败会一直重试
      • cancel:取消执行业务操作,释放预留的资源,如果失败会一直重试
      notion image
  • 事务日志的记录
    • 事务发起者需要记录全局事务和各个分支事务的状态和信息
    • 事务参与者需要记录分支事务的状态
  • 异常处理:
    • TCC 事务在执行过程中的任意环节,均可能发生宕机、重启、网络中断等异常情况,事务会处于非原子状态和非最终一致状态
    • 需要根据主事务记录和分支事务记录的日志,去完成剩余分支事务的提交或者回滚,使整个分布式事务内所有参展达到最终一致的状态,实现事务的原子性。
    • TCC事务解决异常问题,要求业务方在设计实现上要遵循三个策略
      • 允许空回滚:
        • 异常发生在阶段1时,部分参与者没有收到try请求,从而粗发整个事务的cancel操作
        • try失败或没有执行try操作的参与方收到cancel请求时,进行空回滚操作
      • 保持幂等性:
        • 异常发生在阶段2时,若网络超时,TCC会重复调用参与方的confirm/cancel方法,需要两个方法实现上保证幂等性
      • 防止资源悬挂:
        • 网络异常会导致两个阶段无法保证严格的顺序执行,会实现参与方try请求比cancel请求更晚到达的情况,cancel会执行空回滚而确保事务的正确性,但此时try方法也不可以再被执行
  • TCC中事务的ACID特性
    • 原子性:事务发起方协调各个分支事务的全部提交或者全部回滚
    • 一致性:TCC事务提供最终一致性
    • 隔离性:通过try预分配资源的方式来实现数据的隔离
    • 持久性:交给各个分支事务来实现
  • TCC事务模型的缺点:
    • 对业务方侵入较大,需要业务方把功能的实现由一个接口拆分为三个(try,comfirm,cancel),开发成本较高
  • TCC事务模型的有点:
    • 灵活性更好
    • 系统的吞吐量比 2PC/XA 模式要高很多
  • TCC事务的应用:
    • 支持 TCC 事务的开源框架有:ByteTCC、Himly、TCC-transaction。

Saga

与TCC一样是一种补偿事务,没有try阶段,将分布式事务看做一组本地事务构成的事务链
  • 核心思想:
    • 事务链中的每一个正向事务操作,都对应一个可逆的事务操作
    • 事务协调器按照顺序执行事务链中的分支事务
      • 分支事务执行完毕,即释放资源
      • 若某个分支事务失败了,按照反方向执行事务补偿操作
        • 简单来说就是逆序cancel
  • 示例:假设有n个分支事务
    • 执行情况分类
      • T1,T2,...,Tn:n 个事务全部执行成功了。
      • T1,T2,...,Ti,Ci,...,C2,C1:执行到第 i (i<=n) 个事务的时候失败了,则按照 i->1 的顺序依次调用补偿操作。如果补偿失败了,就一直重试。补偿操作可以优化为并行执行。
      • T1,T2,...,Ti (失败),Ti (重试),Ti (重试),...,Tn:适用于事务必须成功的场景,如果发生失败了就一直重试,不会执行补偿操作。
      notion image
  • Saga事务的ACID:Saga事务保障了事务的三个特性
    • 原子性:Saga协调器可以协调事务链中的本地事务要么全部提交,要么全部回滚
    • 一致性:Saga事务可以实现最终的一致性
    • 持久性:基于本地事务实现
    • 隔离性:Saga不保证,原因是本地事务提交后变更就变得对其他事务可见了
      • 会导致事务并行上的问题,需要在业务设计上去规避
  • Saga事务的缺点:
    • 对于业务实现要求高,要求在业务设计上实现三个策略
      • 允许空补偿:网络异常导致事务的参与方只收到了补偿操作指令,因为没有执行过正常操作,因此要进行空补偿。
      • 保持幂等性:事务的正向操作和补偿操作都可能被重复触发,因此要保证操作的幂等性。
      • 防止资源悬挂:网络异常导致事务的正向操作指令晚于补偿操作指令到达,则要丢弃本次正常操作,否则会出现资源悬挂问题。
  • Saga事务和TCC事务的异同
    • 都是补偿事务
      • Saga是不完美补偿,补偿操作会留下之前原始事务操作的痕迹,需要考虑对业务上的影响
      • TCC是完美补偿,补偿操作会彻底清理之前的原始事务操作,用户感知不到事务取消之前的状态信息
    • TCC的事务可以更好的支持异步化,Saga模式在补偿阶段比较适合异步化
  • Saga事务的优点:
    • Saga模式适合于业务流程长的长事务场景,对业务侵入低,非常适合微服务架构的场景
    • Saga事务采用了一阶段提交模式,不会对资源长时间加锁,采用这种模式架构的系统性能高,吞吐高
  • Saga事务的应用:阿里巴巴的 Seata 开源项目和华为的 ServiceComb 开源项目都支持 Saga 模式。

基于消息的分布式事务

  • 核心思想:通过消息系统来通知其他事务参与方自己事务的执行状态
    • 消息系统的引入可以将事务参与方解耦,各个参与方能够异步执行
    • 难点在于解决本地事务执行和消息发送的一致性
      • 两者要同时执行成功或同时取消执行
  • 实现方式
    • 基于事务消息的方案
      • 事务消息:区别于普通消息,事务消息发送成功后,处于prepared状态,不能被订阅者消费,等到事务消息的状态更改为可消费状态后,下游的订阅者才可以监听到消息
      • 处理流程:
        • notion image
        • 事务发起者预先发送一个事务消息
        • MQ系统收到事务消息后,将消息持久化(消息的状态为「待发送」),并给发送者一个ACK消息
        • 事务发起者等待接受MQ系统的ACK消息
          • 如果没有收到ACK消息,取消本地事务的执行
          • 如果收到了ACK消息,则执行本地事务,并给MA系统在发送一个消息,通知本地事务的执行情况
        • MQ系统收到消息后,根据本地事务的执行情况更改事务消息的状态
          • 如果成功执行,将消息更改为“可消费”并择机下发给订阅者
          • 如果事务执行失败,则删除该事务消息。
        • 本地事务执行完毕后,发送MQ的通知消息可能丢失
          • 支持事务消息的MQ系统需要定时扫描消息状态,若存在「待发送」状态的消息,向发消息的发送方发起询问,询问事务消息的最终状态,并根据结果更新事务消息的状态
          • 事务发起方应向MQ系统提供一个事务消息状态查询接口
        • 若事务消息的状态的「可发送」,则MQ系统向下游参与者推送消息,推送失败则不停重试
        • 下游参与者收到消息后,执行本地事务
          • 本地事务如果执行成功,则给MQ系统发送ACK消息
          • 如果执行失败,则不发送ACK消息,MQ系统会持续推送消息
    • 基于本地消息的方案
      • 由于基于事务消息的分布式事务模式对MQ系统要求较高,不是每个MQ系统都支持事务消息,因此基于本地消息的分布式事务模式十分必要
      • 核心思想:事务的发起方维护一个本地消息表,业务执行和本地消息表的执行处在同一个本地事务中
      • 执行流程
        • notion image
        • 业务执行成功,则同时记录一个「待发送」状态的消息到本地消息表中
        • 系统中启动一个定时任务,定时扫描本地消息表中的「待发送」记录,并将其发送到MQ系统中
          • 若发送者失败或超时,则一直重试发送
          • 若发送成功,从本地消息表中删除该记录
        • 后续的消费订阅流程与基于事务消息的模式类似
  • 基于消息的分布式事务模式的ACID特性
    • 原子性:可以实现分支事务都执行或者都不执行。
    • 一致性:提供最终一致性
    • 隔离性:不保障隔离性
    • 持久性:由本地事务来保证
  • 基于消息的分布式事务模式的缺点:
    • 对 MQ 系统的要求较高,对业务实现也有一定的侵入性
      • 要么提供事务消息状态查询接口,要么需要维护本地消息表
    • 不接受回滚:原则上只接受下游分支事务的成功,不接受事务的回滚
  • 基于消息的分布式事务模式的优点:
    • 基于消息的分布式事务可以将分布式系统之间更有效的解耦,各个事务参与方之间的调用不再是同步调用。
    • 适用于对最终一致性敏感度较低的业务场景,例如跨企业的系统间的调用,适用的场景有限。

最大努力通知型分布式事务

基于MQ系统的一种解决方案,但不要求MQ消息可靠
  • 核心思想:通过引入定期校验机制来对最终一致性做兜底,设定了时间窗口,当出现失败超时等问题,会重复调用到达到时间窗口上限,则不再通知。
  • 最大努力通知型分布式事务的特点:
    • 对业务侵入性比较低,对MQ系统要求较低,实现比较简单
    • 适合对最终一致性敏感度比较低、业务链路较短的场景,比如跨平台、跨企业的系统间的业务交互。

分布式事务中间件

  • 阿里的分布式事务中间件有XTS 和 TXC,开源版的分布式事务中间件为Seata

Seata

2019年由阿里云的GTS分布式事务中间件开源而来,Seata 支持 TCC 模式、Saga 模式,
  • AT模式:Seata在TCC模式中提供的一种对业务入侵度为0的解决方案
    • 运行机制:
      • Seata Server协调各个分支事务要么一起提交,要么一起回滚,全局事务基于各个分支事务完成
      • 各个分支事务运行时,Seata Client通过对SQL执行的代理和拦截,解析SQL定位到行记录,记录下SQL执行前后的行数据快照
        • beforeImage 和 afterImage 共同构成了回滚日志,回滚日志记录在独立的表中。回滚日志的写入和业务数据的更改在在同一个本地事务中提交。
      • 分支事务完成后,立即释放对本地资源的锁,然后向Seata协调器上报事务执行的结果
      • Seata 协调器汇总各个分支事务的完成情况,生成事务提交或者回滚的决议,将决议下发给 Seata Client。
        • 如果决议是提交事务,则 Seata Client 异步清理回滚日志
        • 如果决议是回滚事务,则 Seata Client 根据回滚日志进行补偿操作,补偿前会对比当前数据快照和 afterImage 是否一致,如果不一致则回滚失败,需要人工介入。
  • AT模式的特点:AT 模式通过自动生成回滚日志的方式,使得业务方接入成本低,对业务入侵度很低,但是应用 AT 模式也有一些限制
    • AT 模式只支持基于 ACID 事务的关系数据库。
    • AT 模式是通过对 SQL 解析来完成的,对 SQL 语法的支持有限,使用复杂 SQL 时需要考虑兼容性。
    • 目前不支持复合主键,业务表在设计时注意添加自增主键。
    • 全局事务默认的隔离级别是读未提交,但是通过 SELECT...FOR UPDATE 等语句,可以实现读已提交的隔离级别。通过全局排它写锁,可以做到的隔离级别介于读未提交和读已提交之间。

总结

分布式事务的实现模式有很多,两阶段提交是最经典的同步事务协议,也就是将事务拆分为准备阶段和提交/回滚阶段,从而实现分支事务的同时成功或者同时撤销,但存在性能瓶颈和资源锁定问题;而Saga模式通过将事务拆分为一系列本地事务,并在失败时进行补偿,从而实现分布式事务的管理,天然支持异步操作,能够提高系统性能和吞吐量。
从分布式事务的实现上来看,有从资源层实现的XA协议,也有在业务层实现的TCC和Saga模式。此外,为了解耦事务参与方,分布式事务也可以基于消息实现。根据一致性的不同,分布式事务协议可以分为实现强一致性的XA协议,以及实现最终一致性的TCC、Saga和基于消息的分布式事务模式等。
总之,应根据具体场景选择合适的分布式事务方案。

© Shansan 2021 - 2025