分布式事务
事务具有四个特征ACID,分别为原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),在单机数据库中很容易实现,但在分布式数据库中,数据分散在不同的机器上,如何对这些数据进行分布式的事务处理呢?
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上。通常一个分布式事务中会涉及对多个数据源或业务系统的操作。
分布式事务模型和规范
The Open Group提出了一个分布式事务规范-XA,以及分布式事务处理模型-X/OpenDTP模型
X/OpenDTP模型
该模型中定义了三个组件,即Application Program,Resource Manager和Transaction Manager
Application Program AP,即应用程序,可以理解为使用DTP模型的程序,定义了事务边界,并定义了构成该事务的应用程序的特定操作
Resource Manager RM,即资源管理器,可以理解为DBMS系统,应用程序通过资源管理器对资源进行控制,资源管理器能够提供单数据库的事务能力,通过XA接口将本数据库的提交、回滚等能力提供给事务管理器调用,以帮助事务管理器实现分布式的事务管理,资源必须实现XA定义的接口,资源管理器提供了存储共享资源的支持
XA接口是DTP模型定义的接口,用于向事务管理器提供该资源管理器的提交、回滚能力
Transaction Manager TM,即事务管理器,负责协调和管理事务,提供给AP应用程序编程接口并管理资源管理器。事务管理器向事务指定标识,监视它们的的进程,并负责处理事务的完成和失败。事务分支标识(XID)由TM指定,以标识一个RM内的全局事务和特定分支,它是TM中日志与RM中日志之间的相关标记。两阶段提交或者回滚需要XID,以便在系统启动时执行再同步操作
事务管理器还管理着所有的资源管理器,通过它们提供的XA接口来统一调度这些资源管理器,以实现分布式事务
DTP只是一套实现分布式事务的规范,并没有定义具体如何实现分布式事务,TM可以采用2PC、3PC、Paxos等协议实现分布式事务。
AP可以和TM、RM通信,TM和RM可以互相通信,DTP模型中定义了XA接口,TM和RM通过XA接口进行双向的通信
还有其他几个需要注意的概念
- 事务 一个事务是一个完整的工作单元,由多个独立的计算任务组成,这多个任务在逻辑上是原子的
- 全局事务 一次性操作多个资源管理器的事务
- 分支事务 在全局事务中,每一个资源管理器有自己独立的任务,这些任务的集合是这个资源管理器的分支任务
- 控制线程 用来表示一个工作线程,主要是关联AP、TM、RM三者的线程,也就是事务上下文环境,用来标识全局事务和分支事务关系的线程
XA
2PC与3PC
由于在分布式事务中无法得知其他节点的操作结果,为了保证事务处理的ACID特性,需要引入一个协调者来统一调度所有分布式节点的执行逻辑,这些参与事务的节点被称为参与者。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务真正进行提交。根据这个思想提出了2PC和3PC两种协议
两阶段提交协议2PC
两阶段提交协议(Two-phase Commit)2PC经常用来实现分布式事务,这个两阶段提交是相对于单库的事务提交方式来说的,在单库时完成相关的数据操作后,就会直接提交或者回滚,在分布式系统中,在提交之前增加了准备的阶段,所以称为两阶段提交
在两阶段协议中,一般包含两类节点:一类为协调者,一个系统中只有一个;一类为事务参与者。协议中每个节点都会记录操作日志并进行持久化
执行过程
- 阶段一:请求阶段
- 事务询问:协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,等待各参与者响应
- 执行事务:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中
- 反馈事务:如果参与者成功执行了事务操作,反馈给协调者Yes响应,表示事务可以执行;如果参与者没有成功执行事务,反馈给协调者No响应,表示事务不可以执行
- 阶段二:提交阶段,协调者基于第一阶段的结果进行决策:提交或者取消,当且仅当所有参与者同意提交事务,协调者才通知所有参与者提交事务,否则协调者通知所有参与者取消事务
都成功的情况
所有的资源全部成功才算是成功
阶段一
阶段二
失败的情况
准备阶段有一个资源失败就会失败
阶段一
阶段二
可能存在的问题
- 同步阻塞,在进行二阶段提交时,所有参与该事务操作的逻辑都处于阻塞状态,在等待其他参与者响应的过程中,将无法进行其他操作
- 事务参与者发生故障,可能很久都不会回复。需要给每个事务设置一个超时时间,如果某个事务参与者一直不响应,到达超时时间后整个事务失败
- 协调者发生故障,协调者需要将事务相关信息记录到操作日志并同步到备用协调者,加入协调者发生故障,备用协调者可以接替完成后续工作。在这个过程中其他参与者将会一直处于锁定事务资源的状态,无法继续完成事务操作
- 在执行阶段二时,当协调者向所有参与者发送commit请求之后,发生了局部网络异常或者协调者未发送完commit请求之前发生故障,都会导致只有部分参与者收到了commit请求。从而一部分进行了提交一部分没有进行提交出现了数据不一致的情况
三阶段提交协议3PC
由于2PC存在的问题,在其基础上进行改进,出现了3PC协议。将原本的请求阶段再次进行拆分,分为了CanCommit和PreCommit,再加上原本的DoCommit组成了三阶段协议
阶段一:CanCommit
- 事务询问:协调者向所有参与者发送包含事务内容的canCommit请求,询问是否可以执行事务提交操作,等待响应
- 反馈响应:各参与者接到协调者的canCommit请求,如果可以顺利执行事务,反馈Yes响应,并进入预备状态;否则,否则反馈No响应
阶段二:PreCommit
根据参与者的反馈情况决定是否进行PreCommit操作,如果所有参与者都反馈Yes,则进行预提交
- 向所有参与者发送预提交请求,并进入Prepare阶段
- 参与者接收到preCommit请求后,执行事务操作,并将Undo和Redo信息记录到事务日志中
- 如果参与者成功执行了事务操作就会反馈给协调者Ack响应,同时等待最终指令:提交还是取消
如果有参与者反馈了No,则进行事务取消
- 向所有参与者发送abort请求
- 无论是收到来自协调者的abort请求还是等待协调者请求过程中出现了超时,都会取消事务
阶段三:DoCommit
执行提交
- 发送提交请求:协调者处于正常工作状态,且接收到了所有参与者的ack,会从预提交状态转换到提交状态,向所有参与者发送doCommit请求
- 事务提交:参与者接收到doCommit请求进行正式执行事务
- 反馈结果:完成事务提交后,向协调者发送ack消息
- 完成事务:协调者接收到所有参与者的ack消息后,完成事务
取消事务
- 发送取消请求:协调者向所有参与者发送abort请求
- 事务回滚:参与者利用阶段中记录的undo信息来执行事务回滚操作
- 反馈结果:完成事务回滚后,向协调者发送ack消息
- 取消事务:协调者接收到所有参与者的ack消息后,取消事务
可能存在的问题
虽然进行了多次确认,但还是存在数据不一致的情况。在参与者接收到preCommit消息后,如果出现网络分区,协调者与部分参与者无法正常通信,这部分参与者依然会进行事务提交导致数据不一致
一般很少使用这种2PC和3PC,补偿逻辑太复杂了。
消息队列最终一致性
- 先写本地消息表,执行事务成功后,发送mq通知另一个系统进行执行
- 执行事务失败,则由定时任务读取消息表来再次通知另一个系统进行执行