分布式事务之Seata
它把一个分布式事务理解成一个包含了若干分支事务的全局事务。 而全局事务的职责是协调它管理的分支事务达成一致性,要么一起成功提交,要么一起失败回滚。
该图片来自互联网
Seata 中存在几种重要角色
Section titled “Seata 中存在几种重要角色”TC(Transaction Coordinator)
Section titled “TC(Transaction Coordinator)”事务协调者。管理全局的分支事务的状态,用于全局性事务的提交和回滚。
TM(Transaction Manager)
Section titled “TM(Transaction Manager)”事务管理者。用于开启、提交或回滚事务。
RM(Resource Manager)
Section titled “RM(Resource Manager)”资源管理器。用于分支事务上的资源管理,向 TC 注册分支事务,上报分支事务的状态,接收 TC 的命令来提交或者回滚分支事务。
其中 TM 和 RM 是作为 Seata 的客户端与业务系统集成在一起,TC 作为 Seata 的服务端独立部署。
Seata工作流程
Section titled “Seata工作流程”在 Seata 中,分布式事务的执行流程:
- TM 开启分布式事务(TM 向 TC 注册全局事务记录);
- 按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 );
- TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);
- TC 汇总事务信息,决定分布式事务是提交还是回滚;
- TC 通知所有 RM 提交/回滚 资源,事务二阶段结束;
Seata事务模式
Section titled “Seata事务模式”Seata AT模式
Section titled “Seata AT模式”在生产环境上,我实际使用过的模式
该图片来自互联网
在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
Seata AT模式是如何做到对业务无侵入的?
该图片来自互联网
在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性
二阶段提交或回滚
-
二阶段提交:二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
该图片来自互联网 -
二阶段回滚:二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
该图片来自互联网
Seata TCC模式
Section titled “Seata TCC模式”该图片来自互联网
TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法
- Try:资源的检测和预留;
- Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
- Cancel:预留资源释放;
TCC 设计
- TCC 设计 - 允许空回滚。Cancel 接口设计时需要允许空回滚。在 Try 接口因为丢包时没有收到,事务管理器会触发回滚,这时会触发 Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。让事务服务管理器认为已回滚,否则会不断重试,而 Cancel 又没有对应的业务数据可以进行回滚。
- TCC 设计 - 防悬挂控制。悬挂的意思是:Cancel 比 Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,但是 Cancel 比 Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致,所以我们在 Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行 Try 的业务操作。(其实就是幂等)
- TCC 设计 - 幂等控制。幂等性的意思是:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,需要对服务设计时进行幂等控制,通常我们可以用事务 xid 或业务主键判重来控制。在写代码时,TCC三个方法均需保证幂等性
与Seata AT模式比较
相对于 AT 模式,TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。但就是需要业务端写很多代码,这个是缺点,强侵入性
Seata Saga模式
Section titled “Seata Saga模式”长事务解决方案。在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
- Saga模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。
- 不能保证隔离。原因: Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性
Saga 模式不保证事务的隔离性,在极端情况下可能出现脏写。比如在分布式事务未提交的情况下,前一个服务的数据被修改了,而后面的服务发生了异常需要进行回滚,可能由于前面服务的数据被修改后无法进行补偿操作。这时的一种处理办法可以是“重试”继续往前完成这个分布式事务。由于整个业务流程是由状态机编排的,即使是事后恢复也可以继续往前重试。所以用户可以根据业务特点配置该流程的事务处理策略是优先“回滚”还是“重试”,当事务超时的时候,Server 端会根据这个策略不断进行重试。
由于 Saga 不保证隔离性,所以我们在业务设计的时候需要做到“宁可长款,不可短款”的原则,长款是指在出现差错的时候站在我方的角度钱多了的情况,钱少了则是短款,因为如果长款可以给客户退款,而短款则可能钱追不回来了,也就是说在业务设计的时候,一定是先扣客户帐再入帐,如果因为隔离性问题造成覆盖更新,也不会出现钱少了的情况。
Seata Saga模式原理
Section titled “Seata Saga模式原理”基于状态机引擎的 Saga 实现: 它基于事件驱动架构,每个步骤都是异步执行的,步骤与步骤之间通过事件队列流转, 极大的提高系统吞吐量。每个步骤执行时会记录事务日志,用于出现异常时回滚时使用,事务日志会记录在与业务表所在的数据库内,提高性能。
Saga 服务设计经验和TCC类似,内容也是一致的,Saga多了一个 【自定义事务恢复策略】 由于Saga 事务不保证隔离性,在极端情况下可能由于脏写无法完成回滚操作,所以状态机引擎除了提供”回滚”能力还需要提供”向前“执行的恢复操作,让业务最终执行成功。 用户可以根据业务特点配置该流程的事务处理策略是优先“回滚”还是“重试”,当事务超时的时候,服务器端会根据这个策略不断进行重试。
Seata XA模式
Section titled “Seata XA模式”前提:
- 支持XA 事务的数据库。
- Java 应用,通过 JDBC 访问数据库。
XA 协议是由X/Open 公司于1991 年发布的一套标准协议。 XA 是eXtended Architecture 的缩写,因此该协议旨在解决如何在异构系统中保证全局事务的原子性。
在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。 从编程模型上,XA 模式与 AT 模式保持完全一致。
除了Seata还有哪些分布式事务框架?
Section titled “除了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会遇到哪些问题
Section titled “使用Seata会遇到哪些问题”-
在高并发下出现的问题:AT模式获取全局锁失败
Seata的AT、XA模式都是基于全局事务实现的,在高并发的场景下会出现获取全局锁异常,因此这两种模式都不适用高并发场景; Seata TCC模式性能比AT模式的好一点,但是并发量大于100的话还是不适合; 如果基本没有什么并发量的话,可以选择AT模式;并发量在一百内的话可以使用TCC模式 高并发场景,不适合使用Seata,适合用中间件,例如用rocketmq替代Seata,可以弥补Seata的不足
-
高并发超时
容易犯的错误: 服务调用->发现注解->创建事务->
等待锁->获取锁
->业务处理调整代码顺序就可以解决: 用户请求
->等待锁->获取锁
->服务调用->发现注解->创建事务->业务处理 -
还遇到一些字段长度的问题,调整表的字段长度。但是在最新版的Seata上想这些问题都已经解决了