目录
1.绪论
在单体系统中,我们可以通过mysql的本地事务来保证操作的原子性,但是在分布式系统中,不同系统之间通过RPC进行远程调用,协作完成整体功能。这时,我们便需要分布式事务来保证操作的原子性。
2.2PC
2.1 基本原理
2.1.1 组成
要实现2pc主要有两部分组成,分别是事务协调器(TM),资源管理器RM。
事务协调器:事务协调器一般是一个独立的服务,主要用来生成全局事务ID,并且向资源管理器发送预写和提交请求。
资源管理器:资源管理器一般是一个sdk,我们需要实现分布式系统的服务需要依赖这个sdk。它主要是接受事务协调器的预写请求,并且代理业务系统对mysql的操作请求,完成本地事务的提交与回滚。
2.1.2 步骤
2pc主要分成两个阶段,分布式prepare阶段和commit阶段。
1.prepare阶段
步骤如下:
1.客户的发送请求到事务协调器;
2.事务协调器发送prepare请求给资源管理器;
3.资源管理器执行sql,并且记录undolog,但是此时并未commit;
4.如果资源管理器执行sql成功,便给事务协调器返回ack响应。
2.commit阶段
步骤如下:
1.在第一阶段的预提交过程中,如果事务收到所有资源管理器的预提交请求,并且开始对所有资源管理器发送commit;如果资源管理器返回失败或者超时,便对资源管理器发起rollback。
2.执行本地事务,commit或者rollback。
3.资源管理器向事务协调器返回commit结果。
4.资源管理器
2.2 2PC 存在的问题
2.2.1 阻塞问题
这时2pc最核心的问题,即由于2pc在第一阶段的时候只是执行事务操作,但是未commit事务,所以这个时候会占用资源。只有在整个分布式事务操作完成过后,资源才会被释放。
2.2.2 单点故障问题
1. 事务协调器宕机
假设在执行完prepare操作后,将资源锁定。协调者宕机,这些未commit的事务可能一直存在直到本地事务执行时间超过最大执行时间报错。对此,我们可以对资源协调器采用主备方式进行部署,如果主节点宕机,将从节点选为新的TM。当出现新的TM出现时,需要询问所有RM是否有在途的事务,并且完成后续操作。
2.部分数据不一致问题
在第一阶段提交数据过后,在第二阶段进行commit操作的时候,由于网络问题,部分RM执行commit成功,部分节点未收到commit操作,此时肯能导致部分节点数据不一致。
2.资源管理器宕机
prepare阶段宕机:prepare阶段宕机后,TM收不到RM的prepare的ack消息,会让所有的TM进行回滚。
commit阶段宕机:commit阶段宕机过后,其他rm已经执行了commit操作。这个时候TM可以进行重试,如果RM在重试期间恢复,不会导致数据不一致。
3. 事务协调器和资源管理管理器同时宕机
prepare阶段同时宕机:此时还未进行commit操作,新的TM可以通过其他RM获取的接下来应该执行commit或者rollback操作。
commit阶段同时宕机并且宕机时资源管理器未执行commit操作:此时TM可以询问所有RM,获取到当前事务的执行情况,该执行commit或者rollback(因为每个事务有个全局事务id,如果发送了commit请求,RM存有记录)。当宕机的TM恢复过后,执行当前事务操作即可。
可以看出,在TM和RM宕机的情况下,2pc都能保持数据一致性,但是这是建立在TM和RM至少有一个知道当前事务执行状态的请求的情况下,如果在commit阶段的时候,TM和全部RM宕机重启,没人知道当前事务是应该commit还是rollback。此时会导致数据不一致。
2.2 实现
2PC的实现主要有数据库的XA规范,seata的AT模式。
2.2.1 XA规范
XA就是X/Open DTP定义的交易中间件与数据库之间的接口规范,其接口函数有数据库厂商提供。只要数据库支持XA规范,便不需要引入额外的框架,利用该数据库原生的功能实现2PC。
2.2.2 seata的AT模式
seata的AT模式为了解决2pc的第一个问题,也即同步阻塞问题,在prepare阶段会直接进行commit(但是此时会在被修改记录加上行锁,防止其他客户端修改),但是此时会对修改数据加上行锁,防止并且在数据库中记录一条undo log日志,如果在commit阶段,需要回滚,可以根据undo log恢复到以前的数据。
3.3PC
3.1 基本原理
3pc和2pc的最主要的区别是,3pc在2pc的基础上增加了一个cnacommit阶段,降低了资源锁定的时间,另一个就是增加了超时机制,避免了前面说的事务管理器的单点故障问题。
可以看出3PC主要分为3个阶段:
3.1.1 canCommit阶段
1.协调器向管理器发送canCommit请求,询问资源管理器是否可以执行事务操作。
2.资源管理器根据自身情况判断是否可以执行事务操作,并且给协调器返回结果。
3.1.2 prepareCommit阶段
1.如果资源管理器在can commit阶段全部返回yes,协调器会向资源管理器发送prepare请求。
2.资源管理器开始执行事务,并且记录undolog日志,但是此时未commit。
3.执行完成给事务协调器返回ack。
4.如果事务协调器在直未收到资源管理器的ack请求,或者某个资源管理器返回了no,事务协调器发送absort请求,中断事务。
3.1.3 commit阶段
1.当事务协调器收到所有资源管理器的返回,并且都为yes时,向资源管理器发送commit请求。
2.当资源管理器收到commit请求后,commit本地事务。
3.向资源管理器返回ack。
4.如果有资源管理器返回commit失败,或者等待失败,事务协调器向资源管理器发送abort请求。资源管理器利用二阶段提交的undo log日志进行回滚。
3.2 3pc的优缺点
3.2.1 优点
1.增加了cancommit阶段,降低了锁资源的时间。
2.在事务协调器宕机过后,资源管理器会因为长时间未收到事务协调器的消息回滚,不会长时间阻塞资源。
3.2.2 缺点
和2pc一样,在docommit阶段,因为网络原因,资源管理器可能因为长时间未收到事务协调器的commit或者abort请求,导致各个资源协调器的数据不一致。
4.TCC
4.1 基本原理
TCC一般是由业务层实现,主要分为3个阶段,分别是try,confirm和catch。其中第一阶段为try接口实现的业务逻辑,这里一般为业务逻辑的具体实现。如果所有系统都try成功便执行confirm接口,如果某个系统try失败,便执行cancel接口。
可以看出步骤如下:
1.业务应用启动事务;
2.业务应用开始调用各个参与分布式事务的系统提供的try接口。比如库存服务,我们可以在try阶段预占库存,在confirm阶段才扣减库存。
3.如果每个系统都try成功,便执行confirm接口。
4.如果其中有系统try失败或者未收到业务系统对try操作的反馈,便执行cancel接口。
4.2 异常情况分析
4.2.1 confirm失败如何处理
二阶段提交都会存在confirm失败的情况,在前面2pc中,如果commit失败,可以利用prepare记录的undo log日志回滚。在tcc中,一般采用的是重试的方式。如果重试不成功,可以记录日志,认为干预。
4.2.2 允许空回滚
在tcc的第四步,在事务协调器等待业务系统try操作的反馈超时后,会执行cancel操作,但是此时,业务系统并没有收到try请求。
针对对上述问题,我们可以通过幂等性来保证,比如我们可以给每个事务加一个事务id,并且记录事务流水,在执行cancel操作前,我们可以根据事务id查询出当前事务是否执行过try操作,如果没有,便直接返回成功。
4.2.3 防悬挂控制
悬挂控制指的是,在事务协调器等待业务系统try操作的反馈超时后,会执行cancel操作,但是此时,业务系统并没有收到try请求。但是因为前面要求业务系统运行空回滚,所以事务协调器任务事务已经cancel成功。但是后面try请求又到达业务系统,此时应该禁止try操作执行。
解决办法和防止空回滚一样,也是通过事务id保证在执行完cancel操作的事务不会再执行try操作。
4.2.4 幂等性
因为tcc中会存在重试机制,或者网络抖动也可能导致事务重试,所以需要报保证实现tcc业务系统的幂等性。
4.3 TCC的优缺点
4.3.1 优点
1.锁资源的粒度交给业务系统实现,降低锁资源粒度。
2.利用重试机制保证最终一致性。
3.事务协调器也可以交给业务系统实现,避免单点故障。
4.3.3 缺点
业务方需要实现try,cancel, confirm三个接口,代码侵入性高。
5. sega模式
5.1 基本原理
sega模式其实也是二阶段提交。只是sega将一个分布式事务分成多个能够保证幂等性的子事务,并且依次向后提交,并且会给每个子事务保存一个补偿操作。在第一阶段提交的时候,便会直接提交事务。如果存在某个子事务执行失败,便会在第二阶段执行补偿操作。
5.2 补偿策略
sega模式有两种补偿策略,分别是正向模式和逆向模式。
5.2.1 逆向模式
逆向模式其实就是当某个子事务执行Ti执行失败,会按照执行顺序的反方向向后进行回滚。
5.2.2 正向模式
正向模式是当某个子事务执行失败后,会进行重试。这种适合于一定要成功的场景。
5.3 实现方式
5.3.1 命令协调
1.基本原理
其实就是单独的部署一个事务协调器,负责处理每个子事务的执行和回滚。
2.优缺点
优点:
1.服务之间关系简单。
2.业务系统实现简单,只需要接收事务协调器的命令并返回结果。
缺点:
1.事务协调器可能会有单点故障问题。
2.事务过多时,事务协调器可能压力过大。
5.3.2 事件传播
1.基本原理
比如现在分别有3个子事务,订单服务,库存服务,支付服务。订单服务执行完会发布本地事务执行完成的消息,库存服务和支付服务收到消息过后执行本地事务,并且发布消息并且被其他服务监听。
2.优缺点
优点:
1.没有事务协调器,所以没有单点故障的风险。
2.服务少时开发简单。
缺点:
1.可能存在循环依赖的风险。
2.服务多时,事务传播关系复杂。
6.总结
本文讲了,2PC、3PC、TCC、Sega这4中分布式事务的实现方式,它其实对应的就是seata的AT、XA、TCC和sega模式。上述四种方式都是2pc的变种,他们都支持事务执行失败或者成功。这是后文将要介绍的通过消息方式来保证最终一致性的方式的本质区别。
那他们分别适合于什么场景呢?
XA模式是标准的2PC,在第一阶段会锁定资源,在第二阶段才会进行提交,所以它的一致性是最强的,但是性能相对偏低。
AT是对2PC的优化,它在第一阶段便将数据已经提交,第二阶段如果commit失败,可以利用第一阶段保留在数据库中的undo log日志进行回滚。AT和XA一样,都会在数据库中留下undo log日志,所以只能支持Mysql这种关系型数据库的操作。
TCC的其实就是将预提交和提交操作交给业务系统实现,所以它可以在try操作中加入对redis或者es这种非关系型数据库的操作。但是它要求业务系统实现try,confirm,cancel这3个接口,所以相对复杂。
sega主要是针对长事务,比如调用多个第三方接口,或者外部系统的接口。适合采用sega模式,但是需要编写补偿代码(回滚或者重试)。
7.引用
[1]. 七种常见分布式事务详解