【SpringCloud】Spring Cloud Alibaba 之 Seata 分布式事务原理(三十七)

Seata 分布式事务原理 

Seata整体机制

  两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

  • 二阶段:

    • 提交异步化,非常快速地完成。
    • 回滚通过一阶段的回滚日志进行反向补偿。

一阶段

  过程:

  1、解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。

  2、查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

1 select id, name, since from product where name = 'TXC';

    得到前镜像:

idnamesince
1 TXC 2014

  3、执行业务 SQL:更新这条记录的 name 为 'GTS'。

  4、查询后镜像:根据前镜像的结果,通过 主键 定位数据。

1 select id, name, since from product where id = 1;

    得到后镜像:

idnamesince
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 }
View Code

  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 表:数据被删除

   验证二阶段提交成功

原文地址:https://www.cnblogs.com/h--d/p/13034425.html