zoukankan      html  css  js  c++  java
  • 【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 表:数据被删除

       验证二阶段提交成功

  • 相关阅读:
    WCF 第四章 绑定 在多个绑定上暴露一个服务契约
    WCF 第五章 行为 事务跨操作事务流
    WCF 第五章 导出并发布元数据(服务行为)
    WCF 第五章 行为 通过配置文件暴露一个服务行为
    WCF 第五章 不支持会话的绑定的默认并发和实例
    WCF 第五章 并发和实例(服务行为)
    WCF 第五章 行为 总结
    WCF 第四章 绑定 绑定元素
    WCF 第五章 行为 事务之选择一个事务协议OleTx 或者WSAT
    WCF 第四章 绑定 比较各种绑定的性能和可扩展性
  • 原文地址:https://www.cnblogs.com/h--d/p/13034425.html
Copyright © 2011-2022 走看看