Sharding-JDBC分布式事务总结(四)之BASE事务(Seat框架中——AT模式的介绍以及理解)

2023-11-18

1.什么是BASE事务

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。
1、基本可用
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性----注意,这绝不等价于系统不可用。
2、软状态
软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
3、最终一致性
中间状态的数据经过一段时间之后,会达到最终的数据一致性。

2.Seata框架的AT模式

2.1介绍

AT模式是seata框架主推的分布式事务,是对XA事务的升级,核心也是二阶段提交。
一阶段:
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿
AT模式需要启动TC服务端
在这里插入图片描述

2.2原理

这里只做简单介绍,先细化到全局事务中某一分支事务。
全局事务开始…
某一分支事务…
1.通过数据源的代理对业务sql解析,得到sql的表和where条件后查询修改之前的数据。
2.获取此纪录本地锁并执行语句。
3.查询修改之后的数据
4.将修改前后的数据和sql语句插入到undo_log
undo_log表如下, AT模式规定每个需要使用的数据库中都需创建一个undo_log表,用来存放每条修改记录的信息和修改前后的数据。(undo_log见表语句在2.5启动小节)
修改前后数据都存在rooback_info中
rollback_info字段展开,afterImage为修改后数据,beforeImage为修改前数据。
rollback_info字段展开
5.向TC注册分支事务,并且申请关于该记录的全局锁
6.本地事务开始提交,将undo_log数据插入与更新业务数据一并提交,提交后释放此记录的本地锁,允许其他全局事务下的分支事务获取此记录本地锁。
(但是注意只有得到该记录的全局锁后此记录的本地事务才可以提交,本地锁才可以释放,也就是说虽然其他全局事务中分支事务可以拿到此记录的,但是拿不到此记录全局锁,便不可提交本地事务。这是AT模式写隔离部分原理,后面2.3小节还会详细说明。)
7.将本地事务提交的结果告诉TC。
到此这个分支事务的AT模式第一阶段已完成,可以发现此分支事务的本地事务已经提交,数据库表中已经存在该记录修改后的数据。
其他分支事务…
二阶段开始…
8.二阶段回滚:收到tc回滚请求,分支根据undolog表对数据进行补偿,然后删除undolog表中的记录。(注意是补偿操作,并不是回滚,此操作对原sql语句执行逆向操作)
9.二阶段提交,删除undolog(因为在一阶段时分支事务已经提交所以只需删除undolog数据即可)
10.释放每个分支事务记录的全局锁
全局事务…结束
可以注意到整个过程 全局锁 在此全局事务结束前一直是被持有的,所以不会发生 脏写 的问题。

2.3特性(写隔离与读隔离)

根据2.2可以得出,在一阶段时提交后此记录的本地锁便得到释放,其他全局事务的分支事务便可以获取该记录的本地锁执行sql语句,这正是AT模式相对于XA事务的优势——在一阶段时便会释放资源。那么,对于不同全局事务对同一记录的修改,AT模式是如何保证数据最终一致性的·?

AT模式的写隔离

以一个示例来说明:
(本示例来自官网,根据我的理解进行了细化叙述,建议配合2.2小节理解)
两个全局事务TX1TX2,其中TX1中分支事务bt1和TX2的分支事务bt2分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
示例开始前先介绍AT模式的两种锁,AT模式就是依靠这两种锁实现写隔离。
本地锁:只有拿到该记录的本地锁才能对该记录进行写操作,本地事务提交后释放该记录本地锁。本地锁被分支事务持有,可以理解为分支事务结束释放本地锁。
全局锁:只有拿到该记录的全局锁后才能提交该记录的本地事务,之后才能释放本地锁。全局锁被全局事务持有,全局事务结束才能释放全局锁。
1.全局事务TX1先开始,bt1先拿到此记录本地锁,更新操作 m = 1000 - 100 = 900。执行完sql语句,获取此记录全局锁,获取到便可以提交本地事务然后释放本地锁。
2.本地锁释放后此记录的全局锁依然被全局事务TX1持有,所以TX1继续执行分支事务或者是二阶段操作。而此时TX2全局事务的分支事务bx2开始,bx2可以获取到a表m记录的本地锁(bx1一阶段时已释放),执行sql语句m=900-100=800,此时sql语句执行完存在数据库缓存中,并未提交。提交需要先获得m记录的全局锁,而全局锁目前被TX1持有。所以此时bx2需要一直尝试等待m记录的全局锁,并且bx1会一直持有该记录本地锁。
在这里插入图片描述

3.如果TX1一切顺利的话,二阶段提交释放了m记录的全局锁。TX2中的bx2分支事务获取到全局锁进行分支事务的提交,之后再处理其他分支事务。
4.关键在于如果TX1二阶段需要回滚,TX1分支事务需要逐一进行补偿操作。轮到bx1需要补偿了,需要获取m记录本地锁,而此时本地锁被TX2中的bx2持有,bx2需要获取m记录全局锁后才能释放本地锁…经典死锁出现了。AT模式通过“等锁超时“这个机制来避免这个死锁问题。bx1需要获取本地锁进行补偿,此时获取不到,便会一直循环获取。bx2同样一直等待获取全局锁,但是等全局锁时会有超时机制,等锁超时后放弃等待全局锁并回滚(注意因为bx2的本地事务并未提交所以此时是回滚操作而不是补偿)m记录本地事务,本地事务回滚后释放本地锁。bx1获取到本地锁,进行补偿操作。
所以AT模式是通过两把锁来保证数据最终一致性。
而整个过程 全局锁 在 TX1 结束前一直是被TX1 持有的,所以不会发生 脏写 的问题。
这就是AT模式的写隔离

读隔离

AT模式默认事务隔离为读未提交(Read Uncommitted)会出现脏读,而这在数据最终一致性事务下是允许存在的,如果特定情况要实现读已提交,需要使用特定语句 SELECT FOR UPDATE 进行查询(详见官网)

2.4优势(相较于XA事务)

1.分支事务在第一阶段提交完成之后就马上释放本地事务锁定的资源。而XA模式一般是在锁定资源的持续到第二阶段的提交或者回滚后才释放资源。
2.AT模式第一阶段就释放了资源,这样就会让AT模式提升了分布式事务处理效率;
3.之所以在第一阶段就释放了资源,这是因为AT模式下,在第一阶段就记录了undo_log回滚日志,所以不怕第二阶段出现异常。

2.5启动

不知不觉写了蛮多了,关于2.5 seat框架AT模式的启动以及如何在sharding中运行我们放到下一篇总结。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Sharding-JDBC分布式事务总结(四)之BASE事务(Seat框架中——AT模式的介绍以及理解) 的相关文章

随机推荐