分布式事务组件Seata原理

2023年 10月 9日 63.9k 0

分布式事务简介

本地事务

大多数场景下,我们应用只需要操作单一数据库,这种情况下的事务称为本地事务,本地事务的ACID特性是数据库直接提供支持,架构如下:

image.png

分布式事务

在微服务架构中,完成某一个业务功能可能需要横跨多个服务,操作多个数据库,这就涉及到分布式事务。需要操作的资源位于多个资源服务器上,应用需要保证对于多个资源服务器的数据操作,要么同时成功要么同时失败,本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性

应用场景

跨库事务

跨库事务指一个服务同时操作多个库,如图所示:

image.png

分库分表

一个库数据量比较大或者预期未来的数据量比较大,都会进行分库分表,如图所示:

image.png

注意:对于分库分表,通常开发人员会使用数据库中间件来降低sql操作的复杂性,中间件会将操作单库的语法拆分成多条语句,此时要保证同时成功或同时失败,因此数据库中间件都面临分布式事务的问题

微服务架构

如图所示演示了三个服务之间彼此调用的微服务架构

image.png

Service A完成某个功能需要直接操作数据库,同时需要调用Service B和Service C,而Service B又同时操作两个数据库,Service C也操作了一个数据库,需要保证这些服务对多个数据库的操作都同时成功或同时失败,这可能是最典型的分布式事务场景问题

小结

为了保证事务的ACID特性,对分布式事务实现方案而言是很大的挑战,同时还必须考虑性能问题,如果为了严格保证ACID特性导致性能严重下降,对一些需要快速响应的业务也是无法接受的

两阶段提交协议(2PC)

两阶段提交(Two Phase Commit),顾名思义就是将提交过程(commit)划分为两个阶段(Phase)
两阶段提交方案下全局事务的ACID特性,是依赖于RM的,一个全局事务包含多个事务分支,各个事务分支的ACID特性共同构成了全局事务的ACID特性

如图所示

image.png

阶段一

TM(事务管理器)通知各个RM(资源管理器)准备提交它们的事务分支,如果RM判断自己的工作可以被提交,就对内容进行持久化,再给TM肯定答复,否则就是否定答复

以mysql数据库为例,在第一阶段,事务管理器向所有涉及到的数据库服务器发出prepare"准备提交"请求,数据库收到请求后执行数据修改和日志记录等处理,处理完成后只是把事务的状态改成"可以提交",然后把结果返回给事务管理器

阶段二

TM根据阶段一各个RM返回的结果,决定是提交还是回滚事务,如果所有的RM都是肯定答复,TM就通知所有RM进行提交,只要有一个RM否定答复或TM没有收到RM的答复,TM就会通知所有RM回滚自己的事务分支

以mysql数据库为例,如果第一阶段中所有数据库都prepare成功,那么事务管理器向数据库服务器发出"确认提交"请求,数据库服务器把事务的"可以提交"状态改为"提交完成"状态,然后返回应答。如果在第一阶段内有任何一个数据库的操作发生了错误,或者事务管理器收不到某个数据库的回应,则认为事务失败,回撤所有数据库的事务。数据库服务器收不到第二阶段的确认提交请求,也会把"可以提交"的事务回撤

2PC存在问题

  • 同步阻塞问题

    2PC的参与者是阻塞的,在第一阶段收到请求会预先锁定资源,一直到commit才会释放资源

  • 单点故障

    由于协调者TM的重要性,一旦TM发生故障,RM会一直阻塞下去,尤其在第二阶段,TM发送故障,所有的RM还处于锁定事务资源状态,无法继续完成事务操作

  • 数据不一致

    若TM第二阶段发送commit请求时崩溃,可能会导致部分RM收到commit请求而提交事务,部分RM未收到commit请求而放弃事务导致数据不一致问题

Seata

简介

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)

三大角色

  • TC(Transaction Coordinator)事务协调者
    维护全局和分支事务状态,驱动全局事务提交或回滚,TC可以集群部署,解决了2PC协议的单点故障问题
  • TM(Transaction Manager)事务管理器
    定义全局事务的范围,开始全局事务、提交或回滚全局事务
  • RM (Resource Manager)资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚事务

其中,TC为单独部署的Server服务端,TM和RM为嵌入到应用中的Client客户端

生命周期

在Seata中,一个分布式事务生命周期如下:

image.png

1.TM请求TC开启一个全局事务(通过RPC远程调用),TC会生成一个XID作为该全局事务的编号。XID会在微服务的调用链路中传递,保证多个微服务的子事务关联在一起
2.RM请求TC将本地事务注册为全局事务的分支事务,通过全局事务的XID进行关联
3.TM请求TC告诉XID对应的全局事务是进行提交还是回滚
4.TC驱动RM们将XID对应的自己的本地事务进行提交或回滚

Seata AT模式

Seata AT模式的核心是对业务无侵入,是一种改进后的两阶段提交,设计思路如下:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和资源
  • 二阶段:
    • 提交异步化,非常快速
    • 回滚通过一阶段的回滚日志进行方向补偿

一阶段

核心在于对业务sql进行解析,转换成undolog,并同时入库

image.png

RM会去解析SQL(Parse SQL),判断是哪种语句,插入语句的话是没有前置镜像的,删除语句是没有后置镜像的,在执行业务sql(Bussiness SQL)时,会先生成一个前置镜像(Query for Before Image),执行完后生成一个后置镜像(Query for After Image),并且会将回滚日志记录到Undo Log表里面(Insert Undo Log),在提交之前(Before Commit),RM会先请求TC将本地事务注册为全局事务的分支事务(Register Branch),就是将Branch ID和XID关联,然后提交(Commit)释放数据库连接,上报TC(After Local TX)

二阶段

分布式事务操作成功,则TC通知RM异步删除undolog

image.png

分布式事务操作失败

image.png

TM向TC发送回滚请求,RM收到TC发来的回滚请求(Branch RollBack),通过XID和BranchID找到对应的日志回滚记录(Find Undo Log),检查后置镜像是否和当前数据库的值匹配(Check by After Image)

  • 如果匹配,则会根据日志回滚记录的值和前置镜像生成反向SQL并执行(Execute undo SQL),然后删除回滚记录日志(Delete Undo Log)并提交(commit)上报TC(After Local TX)
  • 如果不匹配,相当于有别的线程来改了当前数据库的值,此时我们的分布式事务相当于失败了,导致后续对这个全局事务的请求进不来,Seata是不能解决这个问题的,我们只能实现它提供的finallHandle接口,自己手动处理该问题。(官方建议一个业务参与了分布式事务以后就不要在别的地方提供修改数据库的接口。避免产生该问题,在并发场景下,Seata采用了全局锁(行锁id)来保证)

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论