一文看懂分布式事务
1. 引子
随着互联网的发展,单体架构的系统已很难适应用户规模巨大的业务场景,一旦单体的某个模块出现了问题, 整个系统都会崩盘,于是架构升级,微服务盛行了起来。升级后的架构虽然带来了很多好处,提高了系统的稳定性,但是也带来了不少问题,其中一个比较重要的点就是分布式事务问题。
2. 啥是分布式事务
2.1 单机事务
要说分布式事务,那就要先说说什么是单机的普通事务,在具体的实践当中,单机事务一般是指一个应用,持有一个数据源【数据库连接】,对于一个数据库进行事务操作。这里的事务一般理解为MySQL数据库用innodb存储引擎保障的事务,它具有ACID四个特性:
- 原子性(Atomicity):整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(Consistency):对于并发提交的事务,数据库需要提供并发控制,保证数据库从一个事务的开始状态转换到该事务的结束状态的过程中,中间状态不被其他事务可见,就像事务串行执行一样。
- 隔离性(Isolation):在事务完成以后,即使发生掉电、系统宕机等错误,该事务对数据库所作的更改仍需要持久的保存在数据库中。
- 持久性(Durability):从事务开始到事务结束,数据库的完整性约束(包括外键约束、级联关系、触发器等)不应该因为任何原因被破坏
单体架构之所以能够实现事务,主要原因还是数据库本身支持事务,以MySQL为例,借助实现XA两阶段提交规范以及undo、redo日志等一系列复杂机制的帮助,最终实现了事务。因此,在单体架构中实现的事务,一般都是由数据库自身来保障的,脱离了数据库之后,想要保障事务就得我们自己去实现了。
2.2 分布式事务
系统的微服务化使得以前的【单服务,单数据库】变成了【多服务,多数据库】的架构模式,因此也导致以往在单体应用中的一次数据库连接中的事务操作, 被拆分到了多个应用的多个数据库连接,因此也就无法维持数据库保障的事务特性了。
因此,为了保持事务的ACID特性,我们必须依靠自己的手段来实现借助于数据库本身事务之上的分布式事务,保证在分布式场景下【多服务,多数据库】,也能实现事务。
3. 理论介绍
3.1 CAP理论
CAP定理(也称布鲁尔定理)是分布式计算领域的一个著名理论,与2000年由加州大学伯克利分校的计算机科学家Eric Brewer提出。CAP是以下三个词的缩写:
- 一致性(Consistency):在分布式系统中,一致性指的是所有节点在同一时间具有相同的数据。这意味着,在进行一系列操作后,所有的客户端都应得到相同的数据副本。
- 可用性(Availability):可用性意味着在面对各种失败情况下, 系统仍能继续操作,每个请求都能得到一个(无论数据是否最新)响应。
- 分区容错性(Partition Tolerance):分区容错性是指系统的网络分区(部分节点因网络原因而导致彼此失联,即与其他节点形成“网络分区”)的情况下仍然能够持续提供服务。
CAP定理声称,在一个分布式系统中,以上三个特性最多只能同时满足两个,这已被严谨的数学推理所证明,举个例子,如果我们需要确保系统的一致性和分区容错性,在网络分区发生时,系统可能为了维持数据的一致性选择暂停数据的更新操作,这样会牺牲一部分可用性。
以上是三种选择的简单说明:
- CA without P(一致性和可用性):丢弃分区容错性,保持严格的一致性和高可用性。但在网络分区时,系统无法保证继续工作。在现实场景中,主流的RDBMS(关系数据库管理系统)集群通常就是采用放弃分区容错性的工作模式。以Oracle的RAC集群为例,它的每一个节点都有自己的SGA(系统全局区)、重做日志、回滚日志等,但各个节点是共享磁盘中的同一份数据文件和控制文件的,也就是说,RAC集群是通过共享磁盘的方式来避免网络分区的出现。
- CP without A(一致性和分区容错性):丢弃可用性以保证数据一致性和系统在分区后仍能工作。在现实中,除了DTP模型的分布式数据库事务外,著名的HBase也是属于CP系统。以它的集群为例,假如某个ReginServer宕机了,这个ReginServer持有的所有键值范围都将离线,直到数据恢复过程完成为止,这个时间通常会是很长的。
- AP without C(可用性和分区容错性):在网络分区发生时,系统仍然保持响应,但可能牺牲数据的一致性, 只追求数据的最终一致性,AP系统目前也是分布式系统设计的主流选择。
在实际应用中,分布式系统的设计者通常需要根据业务需求的不同,选择这种CAP的哪两个属性之间取得平衡。
在绝大部分情况下,面对节点和网络的频繁故障,分布式系统的设计不得不考虑处理分区问题。同时,牺牲可用性的代价太过高昂,因为系统的宕机会直接导致显著的收入损失,因此坚持维持分区容错性(P)和高可用性(A)成为必然选择,而不得不放弃一致性(C)。然而,完全放弃一致性并非可行之举,这会导致系统内数据缺乏可信度,从而是的数据毫无价值。出于这种考虑,我们需要确保的是“最终一致性”,这是一种折中方案。只要确保数据最终达到一直的时间点在用户可容忍的范围内,那么稍微牺牲一些即时的强一致性,以换区系统的高度可用性,在互联网行业的多数场合中,这通常是一个更实用的解决方案。
3.2 BASE理论
BASE理论是对CAP定理的一种实践哲学和对传统ACID事务特性的拓展,尤其是对于分布式系统。它强调系统可用性和分区容错性,而不是强一致性,只追求最终一致性。BASE是下列术语的缩写:
- 基本可用(Basically Available):系统可能会因为故障而降级,但仍然提供基本的功能。这可以通过降低某些功能质量来实现,如:延迟更高。性能较低。
- 软状态(Soft State):系统的状态可以不需要立即一致,而是随着时间自行达成一致性。这意味着在不同时间点,系统数据副本不可能是不一样的。
- 最终一致性(Eventual Consistency):在不保证系统数据即时一致的前提下,所有的数据副本最终将在一段时间后趋于一致。最终一致性是一种弱一致性模型,和强一致性相比它允许系统在一段时间内是不一致的,这通常用在大规模的分布式系统中,以提高系统的可用性。
BASE理论的关键在于在分布式系统设计中提供了一种不同于传统数据库ACID特性(原子性、一致性、隔离性和持久性)的灵活性。在面对网络延迟。分区故障、系统负载不均等问题时,BASE理论允许开发者设计更加可伸缩、有弹性的系统,虽然这可能意味着牺牲一些一致性以获得整体系统的可用性和可靠性。
刚性事务满足ACID理论,柔性事务满足BASE理论,追求基本可用,最终一致。
4. 业界分布式事务解决方案
理论讲完了,总要讲些实践,看看业界对于分布式事务,都有哪些解决方案。
- 基于两阶段提交协议的XA事务
- 三阶段提交协议
- TCC事务
- SAGA事务
- 可靠消息通知
- 最大努力通知
4.1 基于两阶段提交协议的XA事务
为了克服分布式事务带来的一致性挑战。1991年,X/Open组织(后并入The Open Group)引入了X/Open XA标准(XA代表拓展结构)。XA事务的主旨是规定了事务管理器(Transaction Manager,负责全局事务协调)与资源管理器(Resource Manager,处理本地事务)之间的交互接口。由一个事务管理器(Transaction Manager)、一个应用程序(Application Program)、以及一个或多个资源管理器(Resource Managers)组成。
- 资源管理器:提供访问事务资源的方法,最典型的资源管理器就是关系型数据库了,另外一种比较常见的资源管理器是消息中间件。
- 事务管理器:协调参与全局事务中的各个事务,需要和参与全局事务的所有资源管理器进行通信。
- 应用程序:定义事务的边界,指定全局事务中的操作。
XA接口提供了双向通讯机制,它作为事务管理器与众多资源管理器间的纽带,通过控制多数据源间的操作协同,确保全局事务能够统一提交或回滚。在Java语境中,XADataSource、XAResource等术语,其实均源自XA标准。
需要注意的是,XA最初并非Java专有规范(因为Java尚未问世),它是适用于各种技术体系的通用标准。Java后续制定了JTA(Java Transaction API,JSR 907)作为全局事务的处理标准。JTA包含两个关键接口:
- 事务管理器接口:javax.transaction.TransactionManager,旨在为Java EE服务器提供容器级事务管理。同时,JTA还包含javax.transaction.UserTransaction接口,允许开发者通过代码手动控制事务的开始、提交和回滚。
- 符合XA标准的资源接口:javax.transaction.XAResource,只要资源(例如JDBC、JMS)实现了XAResource接口,便能支持JTA
XA协议主要采用两阶段提交(2PC)机制来确保分布式事务的一致性。
- 预备阶段:在这一阶段,所有涉及的服务(参与者)被询问是否准备就绪,以便提交事务,并将其可行性反馈给负责协调的实体(协调者)。
- 事务协调者向所有相关的事务参与者发送事务数据,询问它们是否能够进行事务提交,并等候它们的答复。
- 参与者在接收到事务指令后,执行预备事务操作,并在事务日志中记录必要的撤销(undo)和重做(redo)信息,但在这一阶段并不实际提交事务。
- 执行阶段:这一阶段,协调者根据所有参与者返回的准备就绪的信息来决定事务的命运。如果所有参与者都准备好了,协调者则发出提交指令;如果存在任何未准备好的参与者,则发出回滚指令。
- 协调者评估所有参与者的反馈;如果所有参与者都表示可以提交,那么它将指导所有参与者同步地提交事务。
- 如果任何一个参与者表示无法提交,协调者则指导所有参与者进行回滚操作,以撤销事务在所有分支上的影响。
两阶段提交流程图