分布式事务解决方案
# 分布式事务解决方案
传统的单机系统开发中,事物的控制是比较简单的,一般通过事物的隔离级别来达到我们的事物控制。但是如果在分布式系统中我们又该怎么做呢?
这篇文章先讲解一下分布式事务理论:CAP定理和BASE理论,再对具体的方案进行讲解。
# 分布式事务理论
# CAP 定理
CAP 定理,又被称为布鲁尔定理。指一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
2000 年 7 月,加州大学伯克利分校的 Eric Brewer 教授在 ACM PODC 会议上提出 CAP 猜想。2年后,麻省理工学院的 Seth Gilbert 和 Nancy Lynch 从理论上证明了 CAP。之后,CAP 理论正式成为分布式计算领域的公认定理。
名称 | 描述 |
---|---|
Consistency | 一致性,指数据在分布式环境中多个副本保持严格的一致性,即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致。 All nodes see the same data at the same time. |
Availability | 可用性,指服务一直可用,而且每次请求是正常响应时间(但是不保证获取的数据为最新数据)。 Reads and writes always succeed. |
Partition tolerance | 分区容错性,指分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境发生故障 The system continues to operate despite arbitrary message loss or failure of part of the system. |
CAP 权衡:根据CAP定理的描述,我们知道无法同时满足一致性、可用性和分区容错性这三个特性,那要舍弃哪个呢?
- 大多数互联网应用,保证PA,舍弃C(退而求其次保证最终一致性)。对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且互联网应用对低延迟要求越来越高,那么优先考虑保证服务可用性达到 N 个 9,即保证 P 和 A,舍弃C(只要求最终一致性)。虽然某些地方会影响客户体验,但没达到造成用户流程的严重程度。
- 金融领域涉及钱财,必须保证C。对于涉及到钱财这样不能有一丝让步的场景,C 必须保证。
- 一种是保证 CA,舍弃 P:网络发生故障宁可停止服务。貌似这几年国内银行业发生了不下 10 起事故,但影响面不大,报道也不多,广大群众知道的少。
- 还有一种是保证 CP,舍弃 A:例如网络故障是只读不写。
CAP定理要求事物的强一致性(Strong Consistency)!具体的选择孰优孰略,没有定论,只能根据场景定夺,适合的才是最好的。
# BASE 理论
eBay 的架构师 Dan Pritchett 源于对大规模分布式系统的实践总结,在 ACM 上发表文章提出 BASE 理论。
BASE 理论是对 CAP 理论的延伸,核心思想是即使无法做到强一致性(CAP 的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。
名称 | 描述 |
---|---|
Basically Available | 基本可用,是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。例如电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。 |
Soft State | 软状态,指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。例如,分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。MySQL主从同复制延迟也是一种体现。 |
Eventual Consistency | 最终一致性,指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。 |
ACID 和 BASE 的区别和联系:
ACID 是传统数据库常用的设计理念,追求强一致性模型,BASE 支持的是大型分布式系统,提出通过牺牲强一致性获得高可用性。
原子性和持久性必须从根本上保障,为了可用性、性能和服务降级的需要,只有降低一致性和隔离性的要求。BASE 解决了 CAP 理论中没有考虑到的网络延迟问题,在BASE中用软状态和最终一致,保证了延迟后的一致性(弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况)。
总的来说,ACID 和 BASE 代表了两种截然相反的设计哲学,在分布式系统设计的场景中,系统组件对一致性要求是不同的,因此 ACID 和 BASE 又会结合使用。
# 分布式事务解决方案
了解了分布式事务中的强一致性(CAP)和最终一致性理论(BASE),下面介绍几种常见的分布式事务的解决方案:
# 2PC模式(强一致性)
2PC是Two-Phase Commit缩写,即两阶段提交,就是将事务的提交过程分为两个阶段来进行处理。事务的发起者称协调者,事务的执行者称参与者。协调者统一协调参与者执行。
- 阶段 1,准备阶段:协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待所有参与者答复。各参与者执行事务操作,但是打死都不提交事务(MySQL将 undo 和 redo 信息记入事务日志中)。如果参与者执行成功,给协调者反馈 yes;如果执行失败,给协调者反馈 no。
- 阶段 2,提交阶段:如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(rollback)消息;否则,发送提交(commit)消息。
2PC方案实现起来简单,但实际项目中使用比较少,主要是因为以下问题:
- 性能问题:所有参与者在准备或提交阶段都处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。
- 可靠性问题:如果协调者存在单点故障问题,如果协调者出现故障,参与者(没有超时机制)将一直处于锁定状态。就算是某些实现增加选举新协调者、日志记录、参与者相互通知等都无法避免这个问题。
- 数据一致性问题:在阶段 2 中,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就导致了节点之间数据的不一致。
# 3PC模式(强一致性)
3PC 三阶段提交,是两阶段提交的改进版本,与两阶段提交不同的是,同时在协调者和参与者中都引入超时机制。三阶段提交将两阶段的准备阶段拆分为 2 个阶段,新增了一个 preCommit 阶段,解决了原先在两阶段提交中,参与者在准备之后,由于协调者或参与者发生崩溃或错误,而导致参与者处于长时间等待的问题。此外,如果在指定的时间内协调者没有收到参与者的消息则默认失败(二阶段提交也存在)
阶段1,准备阶段(canCommit):协调者向参与者发送 commit 请求,参与者如果可以提交就返回 yes 响应,否则返回 no 响 应。注意,这个阶段参与者并没有开启事务,只是回了一个确认请求,让协调者知道请他们都准备好了。
阶段2,预提交阶段(preCommit):协调者根据canCommit阶段参与者的反应情况执行预提交事务或中断事务操作。
- 参与者均反馈 yes:协调者向所有参与者发出 preCommit 请求,参与者收到preCommit请求后,执行事务操作,但不提交;(MySQL将 undo 和 redo 信息记入事务日志中);各参与者向协调者反馈 ack 响应或 no 响应,并等待最终指令。
- 任何一个参与者反馈 no或等待超时:协调者向所有参与者发出 abort 请求,无论收到协调者发出的 abort 请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。
这个阶段其实跟2PC的准备阶段基本是相似的,主要是参与者多了超时机制以及处于这个阶段时说明参与者都曾回应了协调者,其他参与者都处于一个统一状态-预提交。
阶段3,提交阶段(do Commit):该阶段进行真正的事务提交,根据参与者preCommit阶段反馈的结果完成事务提交或中断操作。这一步与2PC的提交阶段一致。
相比2PC模式,3PC模式降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段 3 中协调者出现问题时(比如网络中断等),参与者会继续提交事务。但是,也有一些问题:
- 性能有所下降,交互过程更长。
- 数据一致性问题:参与者的超时机制,虽然解决了阻塞问题,但是可能存在数据一致性问题。例如,参与者在等待提交命令时候超时了,他提交了事务,但是实际上协调者会执行回滚操作。
总的来说, 2PC 和 3PC 都不能保证数据一致性,因此一般都需要有定时扫描补偿机制。这个两个模式在数据库层面更加适用。
# XA模式(强一致性)
XA是由X/Open组织提出的基于2PC的分布式事务的规范,又被称为标准分布式事务模型DTP(Distributed Transaction Processing)。
XA规范主要定义了全局事务管理器(TM)和局部资源管理器(RM)之间的接口,目前主流的关系型数据库产品(MySQL、Oracla...)都是实现了XA接口。主要包含三个组件,
- AP(Application Program):应用程序,可以理解为使用DTP的程序
- RM(Resource Manager):资源管理器,可以是一个DBMS系统或者消息服务器管理系统,应用程序通过资源管理器对资源进行控制。资源必须实现XA定义的接口。
- TM(Transaction Manager):事务管理器,负责协调和管理事务,提供给AP编程接口(TX协议)以及管理资源管理器(通过XA接口)。
在分布式系统中,从理论上讲两台机器理论上无法达到一致的状态,需要引入一个单点进行协调,所以XA引入了全局事务管理器(TM),由全局事务管理器管理和协调的事务,可以跨越多个资源(数据库)和进程。事务管理器用来保证所有的事务参与者都完成了准备工作(第一阶段),如果事务管理器收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。
- 关系型数据库在这个XA事务中扮演的是参与者的角色,而不是事务管理器,主要实现了XA的接口。
- AP通过向TM开启全局事物,生成全局事务ID,后续的注册资源,可以理解为将子任务注册管理到全局事务ID(TX协议)。
Java通过定义JTA接口实现了XA的模型,这里就不细说了。
# TCC模式(最终一致性)
TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。
TCC 是服务化的两阶段编程模型,其 Try、Confirm、Cancel 3 个方法均由业务编码实现:
- Try 操作作为一阶段,负责资源的检查和预留;
- Confirm 操作作为二阶段提交操作,执行真正的业务;
- Cancel 是预留资源的取消;
TCC事务模式相对于 XA 等传统模型如下图所示:
TCC从思想上看,其实和2PC差不多,但有一点值得注意的是,TCC的撤销和确认是有补偿机制的,需要业务方保持操作的幂等性,由这一点来看,我们将TCC模式归位最终一致性。
TCC从流程看起来比较简单,难点在于业务定义,因为每个操作都需要定义Try-Confirm-Cancel
。
总的来说,TCC 模式相比于 XA,解决了如下几个缺点:
- 解决了协调者单点,由主业务方发起并完成这个业务活动。业务活动管理器可以变成多点,引入集群。
- 降低同步阻塞的影响:引入超时机制,超时后进行补偿,并且不会锁定整个资源,将资源转换为业务逻辑形式,粒度变小。
- 数据一致性:有了补偿机制之后,由业务活动管理器控制一致性。
但是也有缺点:
- 和业务紧耦合,入侵性强,需要针对每个业务操作进行设计。
- 重试时候,需要保证操作的幂等
# 消息队列模式(最终一致性)
消息队列的方案最初是由 eBay 提出,基于TCC模式,消息中间件可以基于 Kafka、RocketMQ 等消息队列。
此方案的核心是将分布式事务拆分成本地事务进行处理,将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或MQ中间件,再通过业务规则人工发起重试。下面描述下事务的处理流程:
- 步骤1,事务主动方在本地事务中,处理业务更新操作以及MQ写消息操作。
- 步骤2,事务主动方通过消息中间件通知事务被动方处理事务,事务消费方接收并处理MQ中的消息。
- 步骤 3,事务被动方通过MQ中间件,通知事务主动方事务已处理的消息,事务主动方根据反馈结果提交或回滚事务。
为了数据的一致性,当流程中遇到错误需要重试,容错处理规则如下:
- 当步骤1 处理出错,事务回滚,相当于什么都没发生。
- 当步骤2 处理出错,由于未处理的事务消息还是保存在事务发送方,可以重试或撤销本地业务操作。
- 如果事务被动方消费消息异常(网络异常等),需要不断重试,业务处理逻辑需要保证幂等。
- 如果是事务被动方业务上的处理失败,可以通过MQ通知事务主动方进行补偿或者事务回滚。
- 如果多个事务被动方已经消费消息,事务主动方需要回滚事务时需要通知事务被动方回滚。
# Saga模式(最终一致性)
Saga 这个概念源于 1987 年普林斯顿大学的 Hecto 和 Kenneth 发表的一篇数据库论文Sagas ,一个Saga事务是一个有多个短时事务组成的长时的事务。 在分布式事务场景下,我们把一个Saga分布式事务看做是一个由多个本地事务组成的事务,每个本地事务都有一个与之对应的补偿事务。
在Saga事务的执行过程中,如果某一步执行出现异常,Saga事务会被终止,同时会调用对应的补偿事务完成相关的恢复操作,这样保证Saga相关的本地事务要么都是执行成功,要么通过补偿恢复成为事务执行之前的状态。(自动反向补偿机制)。
Saga 事务基本协议如下:
- 每个 Saga 事务由一系列幂等的有序子事务(sub-transaction) Ti 组成。
- 每个 Ti 都有对应的幂等补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果。
Saga是一种补偿模式,它定义了两种补偿策略:
- 向前恢复(forward recovery):发生失败进行重试,适用于必须要成功的场景。(头跌,必须成功)
- 向后恢复(backward recovery):发生错误后撤销掉之前所有成功的子事务,使得整个 Saga 的执行结果撤销。
例如上图,Saga两种执行顺序有:
- 事务正常执行完成:T1, T2, T3, ..., Tn,例如:减库存(T1),创建订单(T2),支付(T3),依次有序完成整个事务。
- 事务回滚:T1, T2, ..., Tj, Cj,..., C2, C1,其中 0 < j < n,例如:减库存(T1),创建订单(T2),支付(T3),支付失败,支付回滚(C3),订单回滚(C2),恢复库存(C1)。
# Seata-AT模式(最终一致性)
AT模式来源于阿里开源的Seata框架,本质上是2PC协议的一种实现。
Seata (opens new window)(Simple Extensible Autonomous Transaction Architecture)是一套一站式分布式事务解决方案,是阿里集团和蚂蚁金服联合打造的分布式事务框架。Seata目前的支持的事务模式有AT、TCC、Saga和XA,默认是AT模式。
Seata AT事务模型包含TM(事务管理器),RM(资源管理器),TC(事务协调器),比XA模式多了一个TC。TC是一个独立的服务,需要单独部署,TM和RM以jar包的方式同业务应用部署在一起,它们同TC建立长连接,在整个事务生命周期内,保持RPC通信。
- 全局事务的发起方作为TM,全局事务的参与者作为RM
- TM负责全局事务的begin和commit/rollback
- RM负责分支事务的执行结果上报,并且通过TC的协调进行commit/rollback。
- TC负责协调TM、RM,管理全局事物ID、资源注册等等。
仔细对比,可以发现Seata-AT,其实是对XA-TM的进行了拆分。
在 Seata 中,AT分为两个阶段。第一阶段就是各个阶段本地提交操作;第二阶段会根据第一阶段的情况决定是进行全局提交还是全局回滚操作。具体的执行流程如下:
- TM 开启分布式事务,负责全局事务的begin和commit/rollback(TM 向 TC 注册全局事务记录);
- RM 作为参与者,负责分支事务的执行结果上报,并且通过TC的协调进行commit/rollback(RM 向 TC 汇报资源准备状态 );
- RM分支事务结束,事务一阶段结束;
- 根据TC 汇总事务信息,由TM发起事务提交或回滚操作;
- TC 通知所有 RM 提交/回滚资源,事务二阶段结束;
# 总结
至此,已经介绍完了分布式事务涉及的理论以及解决方案。无论是哪种分布式解决方案其实都是围绕着CAP定理(强一致性)和BASE理论(最终一致性)展开的。
2PC、3PC都是基础的强一致性方案,两者都有数据一致性的问题以及阻塞风险,适用于数据库层面,其中2PC的应用最多。
XA模式是强一致性的,应用得最广,目前很多框架以及DBM都实现了他的接口。
TCC模式、消息队列模式、Saga模式这些都是最终一致性,基本拥有补偿机制,通常适用于要求可用性、接受延迟后的一致性的一些业务系统。
最后的Seata-AT,我认为本质上是2PC的一种实现,同时是对XA模式的一种“拆分”,同时也可以了解一下Seata这款开源工具,毕竟他也支持上面体提到的AT、TCC、Saga模式。