浅谈seata分布式事务框架原理(一)

我爱海鲸 2022-09-23 17:52:57 暂无标签

简介分享一位同事对分布式事务seata的相关理解

前言:

测试环境:两个微服务,分别为customer和goods。

概念:调用方(customer),被调用方(goods)

下图是customer的程序入口,里面调用了goods的feign接口。

这里是seata服务的数据库,存在3张表。

branch_table用来记录当前事务的插入批次信息。批次id用来后续查询undo_log进行回滚

global_table 用来记录当前的请求的全局事务信息。保证整个调用链是同一个全局事务,重点就是xid

事务调用方:

注解的实现类为:

GlobalTransactionalInterceptor类

TransactionalTemplate类execute方法:

注意:commitTransaction这个方法并没有提交数据库事务,只是通知。如果这里打断点的话,是在表里面找不到数据的。

事务被调用方(即feign接口服务类):

SeataHandlerInterceptor类 第一步拦截feign接口请求,查看请求头是否带有TX_XID

然后直接到执行目标的dll方法↓

这里就是常规的mybatis插入。但是!seata通过注入statement的实现类,从而由mybatis底层方法,执行到seata的底层方法

可以说这里就是被调用方seata的入口。PreparedStatementProxy的实际实现类为ExecuteTemplate

实际执行的是父类BaseTransactionalExecutor中的execute方法

然后doExecute方法执行的是AbstractDMLBaseExecutor类里面的doExecute方法
这里只讲executeAutoCommitTrue方法
 
上面截图的register()方法会往branch_table新增一条数据↓记录goods服务的数据库信息
 
 
描述:第一条是我通过↑新增的,而第二条是seata自己记录的
Rollback_info字段里面的值:
{
    "@class":"io.seata.rm.datasource.undo.BranchUndoLog",
    "xid":"192.168.100.66:8191:316614175000367104",
    "branchId":316614175235248100,
    "sqlUndoLogs":[
        "java.util.ArrayList",
        [
            {
                "@class":"io.seata.rm.datasource.undo.SQLUndoLog",
                "sqlType":"INSERT",
                "tableName":"undo_log",
                "beforeImage":{
                    "@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords",
                    "tableName":"undo_log",
                    "rows":[
                        "java.util.ArrayList",
                        [

                        ]
                    ]
                },
                "afterImage":{
                    "@class":"io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName":"undo_log",
                    "rows":[
                        "java.util.ArrayList",
                        [
                            {
                                "@class":"io.seata.rm.datasource.sql.struct.Row",
                                "fields":[
                                    "java.util.ArrayList",
                                    [
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"id",
                                            "keyType":"PRIMARY_KEY",
                                            "type":-5,
                                            "value":[
                                                "java.lang.Long",
                                                84
                                            ]
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"branch_id",
                                            "keyType":"NULL",
                                            "type":-5,
                                            "value":[
                                                "java.lang.Long",
                                                1
                                            ]
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"xid",
                                            "keyType":"NULL",
                                            "type":12,
                                            "value":null
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"context",
                                            "keyType":"NULL",
                                            "type":12,
                                            "value":null
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"rollback_info",
                                            "keyType":"NULL",
                                            "type":-4,
                                            "value":null
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"log_status",
                                            "keyType":"NULL",
                                            "type":4,
                                            "value":null
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"log_created",
                                            "keyType":"NULL",
                                            "type":93,
                                            "value":null
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"log_modified",
                                            "keyType":"NULL",
                                            "type":93,
                                            "value":null
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                }
            }
        ]
    ]
}
可以发现这串json里面记录了字段的主键:id 84。

总结:

调用方通过开启全局seata事务,调用方生成全局事务信息即global_table表数据,生成的xid添加到feigin接口的请求头。被调用方根据xid是否存在,执行是否加入全局事务的逻辑。被调用方会直接提交数据库事务(可以在数据库直接查到新数据),并生成undol_og,和banch_table的数据。

回滚的时候,会通过banchId去查询undo_log的数据,banch_table同时记录了数据库的信息,而undo_log里面记录了新增数据的主键,回滚的时候就可以连接对应的数据库根据主键删掉新增的数据

 
 

你好:我的2025