首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一 .前言
前面说了 Seata Client 的请求流程 , 这一篇来看一下 Client 端对 undo-log 的操作.
undo-log 是 AT 模式中的核心部分 , 他是在 RM 部分完成的 , 在每一个数据库单元处理时均会生成一条 undoLog 数据.
二 . undo-log 表
先来看一下 undo-log 的表结构
1 | java复制代码CREATE TABLE `undo_log` ( |
SQL 语句
1 | SQL复制代码INSERT INTO `seata`.`undo_log`( |
单纯的看 SQL 语句还不是很清楚 , 再详细的看看 , 这里首先透露最终的处理逻辑 , debug 的时候可以通过 DEBUG 该方法进行回退 :
1 | java复制代码// 看一下当前插入的 undoLog 详情 |
undoLogContent 是一个BlobUtils.bytes2Blob 转换的 Byte 数组 , 其中通过 xid 和 BranchId 存储了全局事务 ID (xid) 以及 分支事务 ID(BranchId) ,同时在 sqlUndoLogs 属性中记录了表名 (tableName) 和 操作类型 (sqlType)
此处通过 beforeImage 和afterImage 对前后的数据 (PS : 此处不是备份了整个记录 ,而是备份了部分参数)
三 . Client undo-log 的处理流程
Client 提供了三种保存 undo-log 的实现 , 可以看到 , 都是持久化到库中的 , 只是区分了具体的库类型
3.1 AbstractUndoLogManager 解析
AbstractUndoLogManager 实现了 UndoLogManager , 它是一个主要的管理工具 , 实现了对 undo-log 的管理 , 该类主要实现了如下方法
1 | java复制代码public interface UndoLogManager { |
整个案例中有三个 RM ( Order , Account , Storage) , 下面来看一下三个 RM 是怎么处理的
3.2 undo-log 发起的流程 (Order)
- ConnectionProxy # doCommit : 发起整体的 commit 流程
- ConnectionProxy # processGlobalTransactionCommit : 全局事务提交操作
- UndoLogManagerFactory # getUndoLogManager : 获取 undoLog 管理器
- AbstractUndoLogManager # flushUndoLogs
- MySQLUndoLogManager # insertUndoLogWithNormal
- MySQLUndoLogManager # insertUndoLog : 插入 undoLog
3.2.1 undo-log 的主处理流程
flushUndoLogs是核心流程 , 在该环节中对 BranchUndoLog 进行了查询创建
1 | java复制代码public void flushUndoLogs(ConnectionProxy cp) throws SQLException { |
3.2.2 镜像的查询和获取
镜像是将变动前 (beforeImage) 和变动后(AfterImage)的数据进行了处理 , 来看一下镜像是在哪个环节查询出来的
1 | java复制代码// Image 的起点是 Context 中获取的 |
Step Start : 主逻辑 , 其中查询了前后 Image
1 | java复制代码// 在这个流程中 ,完成了大部分的数据操作 |
Step 1 : 获取 beforeImage
AbstractDMLBaseExecutor 会根据处理的不同有多个实现类
这里仅以Update为例 , insert 时不会查询 , 就不过多的深入了:
1 | java复制代码// C-BaseInsertExecutor : Insert 情况时的处理方式 |
TableMeta 表元数据 :
Step 2 : 查询 afterImage
1 | java复制代码protected TableRecords afterImage(TableRecords beforeImage) throws SQLException { |
Step 3 : 添加 undo-log , 构建 Context ()
1 | java复制代码C- BaseTransactionalExecutor |
这里查询到 Image 后 , 下面再来看一下 image 的插入流程
3.2.3 最终数据的插入
1 | java复制代码protected void insertUndoLogWithGlobalFinished(String xid, long branchId, UndoLogParser parser, Connection conn) throws SQLException { |
补充 :update 下的镜像 undo-log (Storage)
其主流程是和 Order 一致的 , 主要来看一下插入时的 undo-log 数据 , 可以看到 , 这里不是生成了一个 SQL , 而是对字段和数据进行了镜像处理
而且这里的镜像处理的是变动的节点
1 | java复制代码{ |
Account 与此同理 , 这里暂时不说了
四 . Client undo-log 回退流程
上面看完了 undo-log 的创建流程 , 下面来看一下回退时对 undo-log 的处理
这里有个很重要的知识点 , undo-log 的创建是在每个 RM 中创建的 , 但是回滚在
4.1 undo-log 回退流程
Rollback 主流程 :
- RmBranchRollbackProcessor # process : 接收到回退处理请求
- RmBranchRollbackProcessor # handleBranchRollback
- AbstractRMHandler # onRequest
- AbstractRMHandler # handle
- AbstractExceptionHandler # exceptionHandleTemplate
- AbstractRMHandler # handle
- AbstractRMHandler # doBranchRollback : 分支回退
- DataSourceManager # branchRollback
- AbstractUndoLogManager # undo : 执行 undo 逻辑
- AbstractUndoLogManager # deleteUndoLog : 删除分支
这里可以看到 , 最核心的逻辑就是 undo , 这个逻辑的代码比较长 , 我这里分为回调和删除 undo-log 2个逻辑来看 :
4.2 回调主逻辑
1 | java复制代码C- AbstractUndoLogManager |
AbstractUndoExecutor 对 executeOn 进行回退处理
1 | java复制代码public void executeOn(Connection conn) throws SQLException { |
五 . Client undo-log 删除流程
回退完成后 , 再来看一下 undo-log 的删除处理 , 删除逻辑是在 rollback 逻辑之后处理的
5.1 undo-log 主逻辑
1 | java复制代码public void undo(DataSourceProxy dataSourceProxy, String xid, long branchId) throws TransactionException { |
5.2 删除 undo-log
1 | java复制代码 public void deleteUndoLog(String xid, long branchId, Connection conn) throws SQLException { |
总结
这一篇只是归纳了一下 undo-log 的逻辑 ,主要通过 BeforeImage 和 AfterImage 保存前后逻辑 , 用于回退处理
但是这还远远没完 , 后面还有 lock 机制和 远程调用 机制来完善整个流程 , 同时需要梳理出 TCC 的逻辑
本文转载自: 掘金