Seata 分布式事务原理
Seata整体机制
两阶段提交协议的演变:
-
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
-
二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
一阶段
过程:
1、解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。
2、查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。
1 select id, name, since from product where name = 'TXC';
得到前镜像:
id | name | since |
---|---|---|
1 | TXC | 2014 |
3、执行业务 SQL:更新这条记录的 name 为 'GTS'。
4、查询后镜像:根据前镜像的结果,通过 主键 定位数据。
1 select id, name, since from product where id = 1;
得到后镜像:
id | name | since |
---|---|---|
1 | GTS | 2014 |
5、插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。

1 { 2 "branchId": 641789253, 3 "undoItems": [{ 4 "afterImage": { 5 "rows": [{ 6 "fields": [{ 7 "name": "id", 8 "type": 4, 9 "value": 1 10 }, { 11 "name": "name", 12 "type": 12, 13 "value": "GTS" 14 }, { 15 "name": "since", 16 "type": 12, 17 "value": "2014" 18 }] 19 }], 20 "tableName": "product" 21 }, 22 "beforeImage": { 23 "rows": [{ 24 "fields": [{ 25 "name": "id", 26 "type": 4, 27 "value": 1 28 }, { 29 "name": "name", 30 "type": 12, 31 "value": "TXC" 32 }, { 33 "name": "since", 34 "type": 12, 35 "value": "2014" 36 }] 37 }], 38 "tableName": "product" 39 }, 40 "sqlType": "UPDATE" 41 }], 42 "xid": "xid:xxx" 43 }
6、提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁 。
7、本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。
8、将本地事务提交的结果上报给 TC。
二阶段-回滚
1、收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
2、通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
3、数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
4、根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:
1 update product set name = 'TXC' where id = 1;
5、提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。
二阶段-提交
1、收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
2、异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。
Seata原理机制验证
验证项目使用上一章项目,搭建参考:【SpringCloud】Spring Cloud Alibaba 之 Seata 分布式事务中间件(三十六)
项目中TC、TM、RM关系如下:
一阶段-验证
1、调试启动项目,在order项目中,远程调用库存服务扣减库存前,增加断点
2、访问地址:http://localhost:9011/order/create?userId=1&productId=1&count=10&money=100,创建订单
3、当程序运行到断点出,已经运行的创建订单业务,准备调用库存服务,查看数据库(order)中的数据
表order中,有一条数据
表undo_log,有一条数据
其中rollback_info,是一个json,里面内容如下:
beforeImage:表示前镜像,内容为空表示SQL执行前记录未创建
afterImage:表示后镜像,内容为SQL执行后,记录更新后的值
1 { 2 "@class":"io.seata.rm.datasource.undo.BranchUndoLog", 3 "xid":"192.168.1.4:8091:2013220742", 4 "branchId":2013220749, 5 "sqlUndoLogs":[ 6 "java.util.ArrayList", 7 [ 8 { 9 "@class":"io.seata.rm.datasource.undo.SQLUndoLog", 10 "sqlType":"INSERT", 11 "tableName":"`order`", 12 "beforeImage":{ 13 "@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords", 14 "tableName":"order", 15 "rows":[ 16 "java.util.ArrayList", 17 [ 18 19 ] 20 ] 21 }, 22 "afterImage":{ 23 "@class":"io.seata.rm.datasource.sql.struct.TableRecords", 24 "tableName":"order", 25 "rows":[ 26 "java.util.ArrayList", 27 [ 28 { 29 "@class":"io.seata.rm.datasource.sql.struct.Row", 30 "fields":[ 31 "java.util.ArrayList", 32 [ 33 { 34 "@class":"io.seata.rm.datasource.sql.struct.Field", 35 "name":"id", 36 "keyType":"PRIMARY_KEY", 37 "type":-5, 38 "value":[ 39 "java.lang.Long", 40 10 41 ] 42 }, 43 { 44 "@class":"io.seata.rm.datasource.sql.struct.Field", 45 "name":"user_id", 46 "keyType":"NULL", 47 "type":-5, 48 "value":[ 49 "java.lang.Long", 50 1 51 ] 52 }, 53 { 54 "@class":"io.seata.rm.datasource.sql.struct.Field", 55 "name":"product_id", 56 "keyType":"NULL", 57 "type":-5, 58 "value":[ 59 "java.lang.Long", 60 1 61 ] 62 }, 63 { 64 "@class":"io.seata.rm.datasource.sql.struct.Field", 65 "name":"count", 66 "keyType":"NULL", 67 "type":4, 68 "value":10 69 }, 70 { 71 "@class":"io.seata.rm.datasource.sql.struct.Field", 72 "name":"money", 73 "keyType":"NULL", 74 "type":3, 75 "value":[ 76 "java.math.BigDecimal", 77 100 78 ] 79 }, 80 { 81 "@class":"io.seata.rm.datasource.sql.struct.Field", 82 "name":"status", 83 "keyType":"NULL", 84 "type":4, 85 "value":0 86 } 87 ] 88 ] 89 } 90 ] 91 ] 92 } 93 } 94 ] 95 ] 96 }
4、查看 seata 库的
branch_table 表
验证了向TC注册了分支
global_table 表
验证了全局事务ID
lock_table 表
验证了行锁
二阶段-回滚验证
1、当业务出错或超时回滚时,查看 order 库
order 表 :数据回滚
undo_log 表 :被清空、回滚
2、查看 seata 库的
branch_table 表:数据回滚、被清空
global_table 表:数据回滚、被清空
lock_table 表:数据回滚、被清空
验证二阶段回滚成功
二阶段-提交验证
1、当业务正常实现,事务提交,查看 order 库
order 表 :数据改变
undo_log 表 :数据被删除
2、查看 seata 库的
branch_table 表:数据被删除
global_table 表:数据被删除
lock_table 表:数据被删除
验证二阶段提交成功