分布式事务是指在分布式的计算环境中,由多个参与者和多个服务器的协调来实现的一个完整事务处理过程。这个过程涉及到的事务的参与者(如客户端应用程序)、支持事务的服务器(可能是数据库或其他支持事务的组件)、资源服务器(提供服务的服务器)以及事务管理器(负责协调事务的组件),它们都分散地存在于不同的分布式系统或网络中。分布式事务的目标是为了确保当一个大型的操作由多个小型操作组成时,这些操作要么全部成功执行,要么全部失败,以此来维护数据的完整性和一致性。这种机制通常涉及事务的四个基本特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),也被称为ACID属性。简而言之,分布式事务的核心作用是解决在分布式环境下的并发控制和数据一致性的挑战。
一、常见分布式事务框架
- Seata:蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
- ByteTCC:美团点评开源的分布式事务框架,基于TCC补偿机制实现,支持高性能和高可用,提供了丰富的API接口和工具支持。
- Nacos-AT:阿里巴巴开源的分布式事务框架,基于TCC补偿机制实现,可与Nacos和Spring Cloud等分布式框架无缝集成。(已下架)
- SkyWalking-Tx: Apache SkyWalking社区开源的分布式事务框架,支持TCC和Saga等事务模式,提供了完善的监控和追踪功能。(已下架)
- TCC-Transaction:华为开源的分布式事务框架,基于TCC偿机制实现,支持高性能和高可用,提供了简单易用的API接口和工具支持。
- Atomikos:开源的Java事务管理器,支持XA协议,可用于实现分布式事务的控制和管理,提供了简单易用的API接口和工具支持。
二、Seata介绍
Seata是Java领域很强大的分布式事务框架,其支持了多种模式。其中默认支持的AT模式,相比于传统的2PC协议(基于数据库的XA协议),很好地解决了2PC长期锁资源的问题,提高了并发度。Seata支持的各个模式中,AT模式对业务零入侵实现分布式事务,对于开发者更加友好。另外Seata的Server在选择合适的存储介质时可以进行集群模式,减少单点故障影响。
-
功能特性与优势
- 高可用性和数据一致性:Seata 针对分布式事务的处理提供了可靠性和数据一致性方面的保障,能够在任何时候保证分布式事务的完整性和数据的一致性。Seata 能够在可扩展的垂直和水平方向上进行扩展,并支持在各种硬件和操作系统平台上运行。
- 容错处理:Seata 提供高可用性的分布式事务处理,并支持在发生异常时进行容错处理。
- 简单性:Seata 的使用非常简单,可以在应用程序的工作流中轻松地实现分布式事务。
- 高性能:Seata 的分布式事务处理具有高性能,可以在快速和可靠的情况下处理大量的分布式事务请求。
- 开源社区支持:Seata 是一种免费的开源软件,有着强大的开源社区支持和广泛的用户基础。
- 与 Spring 框架的集成:Seata 可以很容易地集成到 Spring 应用程序中,这对于需要进行分布式事务处理的应用程序非常有益。
-
组件介绍
- TC (Transaction Coordinator)
事务协调器,直接调度事务参与者RM,主要负责维护全局事务和分支事务的状态。 - TM (Transaction Manager)
事务管理器,它是事务的发起者,比如:具体的微服务。 - RM(Resource Manager)
资源管理器,其实就是事务的参与者。
以上三个组件相互协作,TC以 Seata 服务器(Server) 形式独立部署,TM 和 RM 则是以Seata Client 的形式集成在微服务中运行。
- TC (Transaction Coordinator)
-
服务端存储模式支持三种 file: 单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高(默认)。
DB: 高可用模式,全局事务会话信息通过DB共享,相对性能差一些。
redis: Seata-Server1.3及以上版本支持,性能较高,存在事务信息丢失风险,需要配合实际场景使用。 -
配置中心支持六种(Seata Client端(TM,RM),Seata Server(TC),会去读取全局事务开关,事务会话存储模式等信息)
- nacos
- consul
- apollo
- etcd
- zookeeper
- file (读本地文件, 包含conf、properties、yml配置文件的支持)
-
注册中心支持八种(Seata Client端(TM,RM),发现Seata Server(TC)集群的地址,彼此通信)
- eureka
- consul
- nacos
- etcd
- zookeeper
- sofa
- redis
- file (直连)
三、Seata的4中模式介绍
AT模式实现的是最终一致性,所以可能存在中间状态,而XA模式实现的强一致性,所以效率较低一点,而Saga可以用来处理不同开发语言之间的分布式事务,所以关于分布式事务的四大模型,基本可以满足所有的业务场景,其中XA和AT没有业务侵入性,而Saga和TCC具有一定的业务侵入。
-
AT(Auto Transaction)
AT模式是Seata默认的工作模式。需要基于支持本地 ACID 事务的关系型数据库,Java 应用,通过 JDBC 访问数据库。
整体机制:该模式是XA协议的演变,XA协议是基于资源管理器实现,而AT并不是如此。AT的2个阶段分别是:
第一阶段:
Seata客户端拦截业务SQL语句,并解析出数据表、主键、字段等信息。
Seata客户端向Seata服务端申请一个全局事务ID (XID)
Seata客户端向数据库发送查询SQL语句,获取数据的前镜像,并缓存在本地。
Seata客户端向数据库发送执行SQL语句,修改数据,并生成数据的后镜像。
Seata客户端将前后镜像和XID一起保存到undo log表中,并提交本地事务
第二阶段:
如果全局事务需要提交,Seata客户端会删除undo log表中对应XID的记录,并结束分支事务。
如果全局事务需要回滚,Seata客户端会根据undo log表中对应XID的记录,生成并执行反向SQL语0句,恢复数据到前镜像状态,并删除undo log表中对应XID的记录。
下图中,步骤1开启全局事务;步骤2注册分支事务,这里对应着一阶段;步骤3提交或者回滚分支事务,对应着二阶段。
特点:
优点:对代码无侵入;并发度高,本地锁在一阶段就会释放;不需要数据库对XA协议的支持。
缺点:只能用在支持ACID的关系型数据库;SQL解析还不能支持全部语法。
需要记录数据的前后镜像,增加了数据库的存储和网络的传输开销。
需要在第一阶段获取全局锁,防止脏写,影响并发性能。
需要在第二阶段根据镜像生成反向SQL语句,可能存在SQL解析和执行的不准确性。 -
XA
XA 模式是基于XA协议实现的分布式事务模式,XA协议是由X/Open组织提出的一种两阶段提交协议,它定义了一个全局事务管理器 (Transaction Manager) 和多个资源管理器 (Resource Manager) 之间的接口。资源管理器通常是数据库或者消息队列等支持本地事务的组件。XA协议要求资源管理器实现以下接口:
start(xid): 开始一个分支事务,xid是全局事务ID。
end(xid): 结束一个分支事务。
prepare(xid): 准备提交或回滚一个分支事务。
commit(xid): 提交一个分支事务。
rollback(xid): 回滚一个分支事务。XA协议的工作流程如下:
第一阶段:
全局事务管理器向所有参与的资源管理器发送start(xid)命令,要求它们开始一个新的分支事务,并将xid作为标识。资源管理器执行各自的业务操作,并锁定相关资源。
全局事务管理器向所有参与的资源管理器发送end(xid)命令,要求它们结束当前的分支事务。
资源管理器结束当前的分支事务,并向全局事务管理器汇报准备就绪状态 (prepared) 或者失败状态0(abort).
第二阶段:
如果全局事务管理器收到了所有资源管理器的准备就绪状态,那么它会向所有参与的资源管理器发送commit(xid)命令,要求它们提交当前的分支事务,并释放相关资源。
如果全局事务管理器收到了任何一个资源管理器的失败状态,或者在超时时间内没有收到所有资源管理器的状态,那么它会向所有参与的资源管理器发送rollback(xid)命令,要求它们回滚当前的分支事务,并释放相关资源。特点:
优点:继承了XA协议的优势,事务具有强一致性。无侵入,将快照数据和行锁等通过 XA 指委托给了数据库来完成。
缺点:同样继承了XA协议的劣势,由于分支事务长时间开启,并发度低。
需要数据库支持XA接口,目前只有MySQL、Oracle、TiDB和MariaDB支持。
需要在第一阶段锁定相关资源,导致资源占用时间长,影响并发性能和可用性。虽然XA带来的无侵入非常高,但是性能下降的程度太大。
需要全局事务管理器维护全局事务状态,增加了系统复杂度和开销。
XA模式的隔离性就是由本地数据库保证,锁存储在各个本地数据库中。由于XA模式一旦执行了prepare后,再也无法重入这个XA事务,也无法跟其他XA事务共享锁。如果没有提交xa事务,或者协调者没有做到二阶段重试,那么这个没有提交的xa事务将会一直 持有锁,造成死锁的局面。
-
TCC(Try-Confirm-Cancel)
TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try) 、确认操作 (Confirm) 、取消操作 (Cancel),他们的具体含义如下:
1.Try: 对业务资源的检查并预留
2.Confirm: 对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功。3.Cancel: 对业务处理进行取消,即回滚操作,该骤回对 Try 预留的资源进行释放。
TCC 是一种侵入式的分布式事务解决方案,以上三个操作都需要业务系统自行实现,对业务系统有着非常大的入侵性,设计相对复杂,但优点是 TCC 完全不依赖数据库,能够实现跨数据库、跨应用资源管理,对这些不同数据访问通过侵入式的编码方式实现一个原子操作,更好地解决了在各种复杂业务场景下的分布式事务问题。TCC模式的工作流程如下:
第一阶段
Seata客户端向Seata服务端申请一个全局事务ID (XID)。
Seata客户端调用各个业务服务的Try操作,预留或锁定相关资源,并记录本地事务日志Seata客户端向Seata服务端汇报各个业务服务的Try操作的状态 (成功或失败)。
第二阶段:
如果全局事务需要提交,Seata客户端会调用各个业务服务的Confirm操作,确认并释放相关资源,并删除本地事务日志。
如果全局事务需要回滚,Seata客户端会调用各个业务服务的Cancel操作,取消并回滚相关资源,并删除本地事务日志。特点:
优点:一阶段提交本地事务,无锁,高性能;事件驱动架构,参与者可异步执行,高吞吐;补偿服务易于实现。
缺点:不保证隔离性。
需要业务实现三个操作,增加了开发和测试的难度和成本。
需要在第一阶段预留或锁定相关资源,导致资源占用时间长,影响并发性能和可用性。
需要保证三个操作的幂等性和一致性,避免出现悬挂或空回滚的情况。三种异常:
空回滚: Try未执行,Cancel 执行了。
幂等: 多次调用方法 (Confirm),出现原因:TC执行confirm或cancel后,因为网络异常或者TC server异常问题,没有收到RM返回的通知,TC会以为没有执行成功,这时候TC就会再次进行调用confirm或cancel,多次对数据做修改,导致幂等性问题。
悬挂: Cancel接口比Try接口先执行,出现原因:超时。场景: try 执行的比较慢,导致调用 try 的服务超时了,由于 Seata 全局事务有超时限制,TM 决议全局回滚,这时候TC就去调了 cancel,调完 cancel 后 try 执行成功了,这时候 try 的资源无法回滚。
-
Saga
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务(执行处理时候出错了,给一个修复的机会)都由业务开发实现。
Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。
Seata分布式三种操作模型中所使用的的微服务全部可以根据开发者的需求进行修改,但是在一些特殊环境下,比如老系统,封闭的系统(无法修改,同时没有任何分布式事务引入),那么AT、XA、TCC模型将全部不能使用,为了解决这样的问题,才引用了Saga模型。比如:事务参与者可能是其他公司的服务或者是遗留系统,无法改造,可以使用Saga模式。Saga模式是Seata提供的长事务解决方案,提供了异构系统的事务统一处理模型。在Saga模式中,所有的子业务都不在直接参与整体事务的处理(只负责本地事务的处理),而是全部交由了最终调用端来负责实现,而在进行总业务逻辑处理时,在某一个子业务出现问题时,则自动补偿全面已经成功的其他参与者,这样一阶段的正向服务调用和二阶段的服务补偿处理全部由总业务开发实现。
Saga状态机:目前Seata提供的Saga模式只能通过状态机引擎来实现,需要开发者手工的进行Saga业务流程绘制,并且将其转换为Json配置文件,而后在程序运行时,将依据子配置文件实现业务处理以及服务补偿处理,而要想进行Saga状态图的绘制,一般需要通过Saga状态机来实现。
基本原理:
- 通过状态图来定义服务调用的流程并生成json定义文件。
- 状态图中一个节点可以调用一个服务,节点可以配置它的补偿节点。
- 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚。
- 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能。
Saga模式的工作流程如下:
第一阶段:
Seata客户端向Seata服务端申请一个全局事务ID (XID)
Seata客户端根据状态机的定义,依次调用各个子事务的正向操作,执行业务逻辑,并记录本地事务日志
Seata客户端向Seata服务端汇报各个子事务的正向操作的状态 (成功或失败)。第二阶段:
如果全局事务需要提交,Seata客户端会删除本地事务日志,并结束分支事务。
如果全局事务需要回滚,Seata客户端会根据本地事务日志,从后往前依次调用各个子事务的反向操作,补偿业务逻辑,并删除本地事务日志。特点:
优点:一阶段提交本地数据库事务,无锁,高性能补偿服务即正向服务的“反向”,高吞吐参与者可异步执行,高吞吐。
缺点:不保证隔离性。在极端情况下可能由于脏写无法完成回滚操作, 比如举一个极端的例子, 分布式事务内先给用户A充值, 然后给用户B扣减余额, 如果在给A用户充值成功, 在事务提交以前, A用户把余额消费掉了, 如果事务发生回滚, 这时则没有办法进行补偿了。这就是缺乏隔离性造成的典型的问题。
需要业务实现正向操作和反向操作,增加了开发和测试的难度和成本。
需要保证反向操作的幂等性和可靠性,避免出现数据不一致的情况。
需要考虑业务的补偿策略和顺序,避免出现业务逻辑的冲突。
三种异常:
空补偿: 原服务未执行,补偿服务执行了。出现原因:原服务超时 (丢包)、Saga 事务触发回滚未收到原服务请求,先收到补偿请求。
悬挂: 补偿服务比原服务先执行。出现原因:原服务超时 (拥堵)、Saga 事务回滚,触发回滚、拥堵的原服务到达。
幂等: 原服务与补偿服务都需要保证幂等性。
seata 的 TCC 模式存在一些异常场景会导致出现空回滚、幂等、悬挂等问题。TCC 模式是分布式事务中非常重要的事务模式,但是幂等、悬挂和空回滚一直是TCC 模式需要考虑的问题,在1.5.1之前的版本需要我们自己加个表去解决,并且需要在编码阶段去处理,侵入性大并且也增加了开发难度。在 1.5.1 版本开始,seata帮我们解决了这些问题。方式就是添加 tcc_fence_log 事务控制表。