一、名词解释:
TC (Transaction Coordinator) - 事务协调者
维护全局/分支事务的状态,驱动全局事务的提交与回滚
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开启、提交、回滚全局事务
RM (Resource Manager) - 资源管理器
管理分支事务(本地事务)与TC交互,注册、报告分支事务状态,管理分支事务的提交、回滚
二、seata支持的模式
1、AT
如上图如果使用的是AT模式,需要在每个DB创建一个UNDO_LOG 表,把业务Sql和前后镜像SQL租出一条回滚记录插入该表
整体机制
一阶段:业务数据和回滚日志记录在同一个事务中提交,提交完释放数据库连接,释放本地锁
二阶段:分支事务全部成功,异步提交(删除回滚日志);分支事务有失败,通过一阶段的回滚日志进行补偿
注意点:
1、t1,t2两个线程操作一条数据,t1分支事务提交前会获取全局事务,如果t1不释放全局事务,t2分支事务提交时获取全局锁会等待,直到t1全局事务是否或者t2超时退出
2、AT中每个分支事务都是独立的,只要不更新同一条数据就没什么关系,即便发生更新同一条数据也只会在真正提交的时候等待全局事务,t1,t2几乎是并行的
3、t2一阶段正持有本地锁并且等待获取全局锁;t1第二阶段回滚,需要获取本地锁,这个时候就出现了循环等待的情况(也就是我们常说的死锁条件),这个时候为了避免脏写,需要t2获取全局锁超时,释放本地锁,t1获取到本地锁执行回滚补偿
具体过程
- 解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。
- 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。--一次查询
- 执行业务 SQL:更新这条记录的 name 为 'GTS'。-- 一条update
- 查询后镜像:根据前镜像的结果,通过 主键 定位数据。--一次查询
- 插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到
UNDO_LOG
表中。--一条insert - 提交前,向 TC 注册分支:申请
product
表中,主键值等于 1 的记录的 全局锁 。 - 本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。--一次更新(3和5一次本地事务提交)
- 将本地事务提交的结果上报给 TC。
期间多了两次查询,一条insert语句,比直接更新效率低了不止一倍
2、TCC
1、需要业务编写prepare ,commit,rollback
2、全局管理分支事务,不存在等待,需要自己维护业务逻辑(比如维护一个中间态,扣款的时候先冻结)
3、saga
1、每个业务都需要单独实现数据的正向操作和逆向操作
2、如果分支业务都正常,事务结束;如果分支业务异常,可正向或逆向补偿
4、XA
1、执行业务前先获取全局事务,不存在分支事务情况
2、t1未释放全局事务之前,t2一直等待或者超时
3、最长得等时间=最长的分支RM
三、总结:
AT和XA
1、AT不需要单独配置,XA方式一:要求开发者配置 XADataSource,方式二:根据开发者的普通 DataSource 来创建但需要创建出相应的 XAConnection
2、AT分支事务单独管理释放,XA需要等待每个分支事务都释放(所有的分支事务时间依赖于最长的分支事务)
3、AT需要建UNDO_LOG表记录回滚日志,XA不需要建表
TCC和Saga
1、TCC需要实现三个接口,Saga业务维护状态通过补偿实现一致性
总体来说:
1、TCC,Saga需要业务代码侵入,并且是每个业务都要写一套
2、XA对接简单,并发量支持有限
3、AT如果链路较长,更新频繁,也可能存在等待时间较长
题外话:saga是不是有点像平时写代码不要事务,用job或MQ做最终一致性的样子