分布式事务DTP模型
DTP模型是Distributed Transaction Processing的缩写,DTP是一套分布式事务的规范,不同的厂商针对此规范提供实现。DTP中包含AP、RM、TM三个角色,其中:
- AP(Application Program):应用程序,就是我们使用分布式事务的应用;
- RM(Resource Manager):资源管理器,表示参与分布式事务的资源,比如数据库,MQ等;
- TM(Transaction Manager):事务管理器,是分布式事务的协调者。
它们之间的关系如下:
在DTP模型中,AP操作RM完成事务中的数据操作,比如数据库的更新等,在这些操作结束后,AP通过TX接口向TM发起提交事务或者回滚事务,TM通过XA协议向RMs提交或者回滚事务,XA通过两阶段提交的方式完成事务的提交或回滚。
DTP和XA的缺点:
- 缺少事务补偿机制,2PC有数据不一致的风险,如果多个RM只有一部分提交成功,其它RM在提交事务时出现超时等错误,无法进行事务的补偿,会造成数据不一致
- XA协议会导致资源的阻塞,在整个事务的过程中,RM资源会一直被事务持有,直到事务提交或回滚
- 性能非常低
- 并不是所有资源都支持XA协议,mysql5.7以前的版本对XA的支持不太好
两阶段提交(2PC)
前面说过XA协议通过两阶段提交的方式做事务的提交或回滚,两阶段提交,即将事务的提交分为两个阶段:
- 第一阶段:prepare阶段,用于向RM询问事务是否可提交,至于RM如何实现prepare则完全靠RM,比如记录redo和undo日志
- 第二阶段:commit/rollback,如果所有RM的prepare执行后都表示可以提交,则依次commit,否则依次rollback
两阶段提交的示意图可表示如下:
如果有RM的prepare阶段失败,则需要回滚,示意图如下:
2PC的缺点:
- 整个事务过程中,所有资源都是阻塞式持有
- 性能差
- 会有数据的不一致风险,当有部分资源提交成功部分不成功时,数据不一致
- TM容易成为逻辑单点,当TM宕机,事务状态难以恢复
Seata对分布式事务的支持
这里先介绍三个概念:
- 全局事务:表示一次分布式事务,由TM发起
- 分支事务:每个RM都有一个事务,被称为分支事务
- XID:全局事务的事务ID
Seata借鉴了DTP模型中的概念,并且自定义了TC(Transaction Coordinator)角色,实际上是将事务的协调者从TM中分享出来并独立部署,TC负责全局事务的提交和回滚,并对分支事务做补偿,使得即使事务提交过程各,部分分支事务成功部分失败,也能达到最终一致
Seata支持的分布式事务的模式
AT模式
AT模式是建立在数据库的ACID事务的基础上的,它提供的两阶段提交是对XA的两阶段提交的演进,效率上比XA好,但是整体开销还是偏大。AT模式的基础原理是在每个分支事务的数据库中记录事务的回滚日志,seata对事务的处理过程如下:
开启全局事务,向TC获取事务ID
依次执行分支事务
开启分支事务
通过解析sql的方式拿到sql的表、更新字段、条件等
根据条件查询,获取到事务更新前的镜像
执行更新
再次查询,获取 到更新后的镜像
通过后镜像生成redo log,插入到分支事务所在的库中
提交分支事务前,先向TC注册分支事务,并获取操作的数据的id对应的全局锁,如果获取失败,全局锁被其它事务持有,则回滚
提交分支事务,并向TC上报分支事务执行结果
提交全局事务,释放全局锁
当事务需要回滚时,每个分支事务的DB中有redo log,RM通过redo log执行事务的回滚,但回滚时需要对数据进行判断,如果当前数据与redo log中的兵团镜像相同,则回滚,否则表示数据被事务外的操作修改了,需要根据配置策略做处理。AT模式下的两阶段提交行为如下:
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚
TCC模式
Seata支持TCC模式,TCC模式也是基于两阶段提交的,与AT模式不同的是,TCC不依赖数据库的ACID特性,而是依赖应用自定义的一阶段的prepare行为和二阶段的commit/rollback行为,TCC模式将事务从数据库层面提升到了应用服务层面。
在TCC模式中,分支事务需要实现prepare、commit和rollback三个方法,其示意图如下:
总体过程比较清晰:
TM开启全局事务,获取XID
依次执行分支事务
向TC注册分支事务
执行事务的prepare阶段
提交或者回滚全局事务,TC向每个RM发起commit或者rollback
TCC模式下,两阶段行为如下:
- 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
- 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
- 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
Saga模式
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
说简单点,就是第一阶段,每个分支事务先自己提交,当需要回滚的时候,分支事务提供一个回滚的入口,对事务做逆向过程:
Saga模式有一定的优势:
- 参与者可异步执行
- 性能高,一阶段提交本地事务
- 补偿服务易于实现
AT、TCC、Saga三种模式的比较
AT模式:
AT模式的性能低,有全局锁,一次分支事务多一次SQL解析和两次查询一次插入,成本比本地事务高得多,这些DB操作是实现回滚的代价
全局锁有死锁的风险
保证了隔离性,全局锁能实现读隔离和写隔离,详情可以阅读seata的文档
基于本地数据库的ACID特性,代码改造代价低
TCC模式:
代码改造代价大,需要将事务拆分成prepare+commit并提供rollback
性能好,不会阻塞资源
将两阶段由应用自身定义,可达到较高的隔离性
Saga模式:
代码改造代价比TCC模式小,但比AT模式大,需要提供回滚补偿接口
性能好,第一阶段就提交了分支事务
没有隔离性
AT、TCC、Saga三种模式,是在性能、改造成本、隔离性三者之间做权衡和取舍,AT选择了隔离性和低改造成本,TCC选择了性能和隔离性,Saga选择了性能和低改造成本,如下图所示:
基于事务消息的分布式事务方案
还可选择使用RocketMQ的事务消息来实现分布式事务。
优点:
- 实现简单,改造成本小
- 性能高,没有全局锁,也没有两阶段的开销
同样有缺点和限制:
- 没有隔离性的支持
- 弱一致,对于库存扣减类场景不适用
- 事务消息一旦提交,全局无法回滚
如果采用事务消息实现分布式事务,需要有其它方案规避其缺点,这里不做描述。