1. 分布式事务产生的背景
1.1 数据库水平拆分
对于大部分的业务而言,在起步阶段,为了快速上线,一般都是单库单表的。但是随着业务的扩张,数据量也随着扩增,单库的性能逐渐变差,就会有数据库的单点压力。因此我们就需要考虑数据库的分库分表方案了。分库分表的目的在于减少单库的负担,提高读写性能。策略可以归纳为拆表和拆分库,拆表是把表的字段进行拆分,即一张表拆分为多张表,这样就使得单表的行数降低,提升查询效率。但是单纯的库内分表只解决了单表的数据过大的问题,并不能解决在同一数据库服务器上的硬件瓶颈,因此需要同时考虑拆库,将原来的单库单表进行拆分数据片,如下图所示:
分库分表之后,原来在一个数据库上就能完成的写操作,可能就会跨多个数据库,这就产生了跨数据库事务问题。
1.2 SOA微服务化
随着业务以及技术的演进,单体架构的瓶颈也越来越明显。按照面向服务的架构(SOA)的原则,我们需要一个既能解决业务之间耦合性,又具备高可用、可伸缩的需求越来越强烈。在将单体架构,拆分为多个业务服务之后,每块业务只需要关注自己的服务即可,形成了一种高内聚、低耦合的架构设计方案,此时简易系统架构图如下:
业务系统按照服务拆分之后,一个完整的业务往往需要调用多个服务,此时,不可避免地会产生数据不一致的问题。由于涉及到多个服务以及多个数据库,本地事务肯定无法满足需求,因此分布式事务就登上了舞台。简单一句话概括分布式事务:为了保证不同服务、不同数据库的数据一致性的事务解决方案。
2. CAP原则
在计算机科学理论中,CAP定理指出,在一个分布式系统中,不可能同时满足以下三点,最多满足两个:
- 一致性(Consistence),所有节点每次读写都能保证获取最新数据,即分布式系统内所有节点的数据副本保持强一致性;
- 可用性(Availability),每次请求数据都能得到响应,但不一定是最新的数据,无论任何故障产生后,需要保证服务仍然可用;
- 分区容错性(Partition Tolerance),被分区的节点可以正常对外提供服务;
对于常规的分布式系统而言,分区容错性是一个最基本的要求,因此,在分布式事务解决方案时,只能选择PC或者AP的模式。
3. 基础协议
3.1 XA协议
在讲分布式事务之前,必然需要先了解XA协议。XA是一个协议,是X/Open组织制定的关于分布式事务的一组标准接口,实现这些接口,便意味支持XA协议。XA协议制定的主要标准有:
- 定义了分布式事务参与方的两个角色,事务协调者和事务参与者;
- 定义了相关接口,只有定义无实现;
其中二阶段提交(2PC)就是XA分布式事务标准的一种实现协议。
3.2 两阶段提交协议
两阶段提交协议包含的术语:
- TM(Transaction Manager),事务管理器,生成全局的事务id,并把多个本地事务协调为全局的分布式事务;
- RM(Resource Manager),资源管理器,事务的参与者;
二阶段提交的算法思路可以概括为如下图:
两阶段提交协议将分布式事务的提交拆分为2个阶段:
第一阶段:准备阶段
TM给每个RM发送prepare消息,用于预留事务所需的资源,如果每个RM都资源预留成功,则进行第二阶段资源提交,否则协调RM回滚资源。
第二阶段:提交阶段
如果TM收到了RM的失败消息或者超时,直接给每个RM发送回滚(Rollback)消息;否则,发送提交(Commit)消息;RM根据TM的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)
2PC可能存在的一些问题:
- 执行过程中,所有参与节点都是事务阻塞型的,可能会存在数据库资源锁定时间过长以及TM等待时间过长的问题,这会导致系统整体的并发吞吐量变低;
- 单点故障问题,TM在两段提交中具有举足轻重的作用,一旦TM故障,整个系统将无法工作,因此需要投入巨大的精力来保障TM的高可用性;
- 必须假设网络在提交阶段的短时间内是可靠的,在阶段二中,如果TM向RM发送commit请求之后,发生了网络异常,会导致只有一部分RM接收到了commit请求,没有接收到commit请求的RM最终会执行回滚操作,从而造成数据不一致问题;
3.3 三阶段提交协议
三阶段提交协议流程示意图如下:
- 阶段一CanCommit,TM协调者向参与者发送can_commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应,这是比2PC多的一步骤,提前询问各个节点是否准备成功,防止超时;
- 阶段二PreCommit,根据阶段一的反馈结果分为两种情况,情况1假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行;情况2假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断;
- 阶段三DoCommit,该阶段进行真正的事务提交;
可以看出,3PC可以解决单点故障问题,并减少阻塞问题,。三阶段提交协议引入了超时机制,一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit,而不会一直持有事务资源并处于阻塞状态。
但是3PC对于数据一致性问题并未有任何改进,比如在进入PreCommit阶段后,如果协调者发送的是abort指令,而此时由于网络问题,有部分参与者在等待超时后仍未收到Abort指令的话,那这些参与者就会执行commit,这样就产生了不同参与者之间数据不一致的问题。
由于3PC非常难实现,目前市面上主流的分布式事务解决方案基本都是2PC协议。
4. 最终一致性解决方案
4.1 TCC模式
TCC是柔性事务的一种,即追求最终一致性,并不要求每一步的强一致性,TCC采用补偿机制达到最终一致性。针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。TCC是Try、Confirm、Cancel三个单词的缩写,其本质是一个应用(业务)层面上的2PC协议实现:
- 准备阶段,TM协调者调用每个节点提供的try接口,将整个全局事务涉及到的资源锁住,若锁定成功,try接口向TM返回yes状态;
- 提交阶段,若所有RM节点的try接口在阶段一都返回yes,则进入提交阶段,TM调用所有服务的confirm接口,各个RM节点进行事务提交。如果有任何一个服务的try接口在阶段一返回no或者超时,则TM协调者调用所有服务的cancel接口。
所以TCC的实际执行情况,要么是Try->Confirm,要么是Try->Cancel。
TCC是如何解决二阶段单点故障问题的呢?答案是重试机制。由于try操作锁住了全局事务涉及的所有资源,保证了业务操作的所有前置条件得到满足,因此无论是confirm阶段失败还是cancel阶段失败都能通过不断重试直至confirm或cancel成功。
由于有TM的重试机制,RM节点confirm和cancel的接口实现必须是幂等的。
4.2 Saga模式
Saga模式的思想,来源于这篇论文,作者提出了将一个长事务,分拆成多个子事务,每个子事务有正向操作Ti,反向补偿操作Ci。
- 假如所有的子事务Ti依次成功完成,则全局事务完成;
- 假如子事务Ti失败,那么会调用Ci, Ci-1, Ci-2 …进行补偿;
Saga模式的使用场景:
- 业务流程长、业务流程多(订票、订餐);
- 其他具有长事务特点的业务;
4.4 基于消息队列的最终一致性解决方案
无论是2PC、3PC还是TCC模式,基本都遵守XA协议的规范,即这些方案本质上都是事务协调者协调各个事务参与者的本地事务的进度,使所有本地事务共同提交或回滚,最终达成一种全局的ACID特性。在协调的过程中,协调者需要收集各个本地事务的当前状态,并根据这些状态发出下一阶段的操作指令。
但是这些全局事务方案由于操作繁琐、时间跨度大,或者在全局事务期间会排他地锁住相关资源,使得整个分布式系统的全局事务的并发度不会太高。这很难满足高并发场景对事务吞吐量的要求。
基于消息中间件的最终一致性全局事务方案是互联网公司在高并发场景中探索出的一种创新型应用模式,利用MQ实现微服务之间的异步调用、解耦合和流量削峰,支持全局事务的高并发,并保证分布式数据记录的最终一致性。
目前RocketMQ、Pulsar均支持了事务性消息。
5. 企业级分布式事务解决方案
前面几节我们介绍了分布式事务的理论基础,本小节我们再来了解一下目前业界比较热门的分布式事务落地方案。
5.1 Seata
Seata是一款阿里开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。关于Seata的详细介绍可参考文章:Spring Boot(七)之基于Dubbo和Seata的分布式事务解决方案
如下图所示,Seata中有三大模块,分别是TM、RM和TC。 其中TM和RM是作为Seata的客户端与业务系统集成在一起,TC作为Seata的服务端独立部署。
Seata支持4种分布式事务解决方案,分别是AT模式、TCC模式、Saga模式和XA模式。
在Seata中,分布式事务的执行流程:
- TM开启分布式事务(TM向TC注册全局事务记录);
- 按业务场景,编排数据库、服务等事务内资源(RM 向TC汇报资源准备状态 );
- TM结束分布式事务,一阶段结束(TM通知TC提交/回滚分布式事务);
- TC汇总事务信息,决定分布式事务是提交还是回滚;
- TC通知所有RM提交/回滚资源,二阶段结束;
5.2 DTM
DTM是一款golang开发的分布式事务管理器,目前star数4.6k,它解决了跨数据库、跨服务、跨语言栈更新数据的一致性问题。他优雅的解决了幂等、空补偿、悬挂等分布式事务难题,提供了简单易用、高性能、易水平扩展的解决方案。
DTM的主要优点如下:
- 易接入:零配置启动服务,提供非常简单的HTTP接口,极大降低上手分布式事务的难度,新手也能快速接入;
- 跨语言:可适合多语言栈的公司使用。方便go、python、php、nodejs、ruby、c# 各类语言使用;
- 使用简单:开发者不再担心悬挂、空补偿、幂等各类问题,首创子事务屏障技术代为处理;
- 易部署、易扩展:依赖mysql|redis,部署简单,易集群化,易水平扩展;
- 多种分布式事务协议支持:TCC、SAGA、XA、二阶段消息,一站式解决所有分布式事务问题;
6. 参考文档
- 分布式事务Seata Saga模式首秀以及三种模式详解
- 分布式事务中间件Seata的设计原理
以上内容就是分布式事务总结的全部内容了,谢谢你阅读到了这里!
Author:zhaoyh