事务恢复主要由 XARecoverTask 负责 。 其主体代码为 recoverInstance 方法 , 该方法检查一个 DN 下的所有 prepare 过的事务 , 并根据事务状态提交或回滚这些事务 , 代码如下:
// XARecoverTask#recoverInstance(IDataSource SetString)// dataSource 用户获取 DN 的连接 , groups 是当前逻辑库的所有物理分片private void recoverInstance(IDataSource dataSource SetStringgroups) { // 执行 XA RECOVER 获取该 DN 上所有 prepare 的事务 。// 此处省略了从 dataSource 获取连接和生成 statement 的代码 。ResultSet rs = stmt.executeQuery(\"XA RECOVER\"); while (rs.next()) { // 对每一条记录 , 生成对象 PreparedXATrans ,// 主要包括 xid , 事务id , 分支事务所在的分片 , 主分片等信息 。PreparedXATrans trans = /* 从 rs 中获取一行数据生成 */; if (/* trans 所在分片是当前逻辑库的一个物理分片trans 在上一次 recover 任务时也出现过 ,即较长时间都未被提交或回滚*/) { // 介入处理这个分支事务:回滚或提交 。rollBackOrForward(trans stmt);该任务每 5 秒到每个 DN 上执行一次 XA RECOVER , 得到所有 prepare 过的事务 , 如果看到一个事务在上一次任务中也出现过 , 即至少过去 5 秒都没有提交或回滚 , 则会选择对这个事务进行回滚或提交 。 相关逻辑代码为 rollBackOrForward , 代码如下:
// XARecoverTask#rollBackOrForward(PreparedXATrans Statement)private boolean rollBackOrForward(PreparedXATrans trans Statement stmt) throws SQLException { String primaryGroup = /* 从 trans 里解析出主分片 , 详见前文关于 xid 的生成 */; // 尝试从主分片的事务日志中找到相关的事务日志 。GlobalTxLog tx = GlobalTxLogManager.get(primaryGroup trans.transId); if (tx != null) { // 确实存在事务日志 , 根据事务日志状态判断回滚或提交 。if (tx.getState() == TransactionState.ABORTED) { // ABORTED 状态的事务需要回滚 。return tryRollback(stmt trans);else { // SUCCESS 状态的事务需要提交 。return tryCommitTSO(stmt trans tx.getCommitTimestamp());else { // 没有找到事务日志 , 尝试回滚 , 先开启事务 , 写下事务日志 , 标记事务为 ABORTED 。try (Connection conn2 = /* 获取主分片上的另一个连接 */;) { conn2.setAutocommit(false); // 尝试回滚 , 如果因为别的线程正在处理这个事务 //(比如该事务只是提交得慢 , 连接仍未断开 , 还在提交流程) // 而报错 , 就回滚上一条写事务日志的语句 。txLog.append(transInfo.transId TransactionType.XA TransactionState.ABORTED new ConnectionContext() conn2); stmt.execute(\"XA ROLLBACK \" + trans.toXid()); conn2.commit(); return true;catch (Exception e) { /* 根据异常判断是否要回滚写事务日志的操作 */该方法主要有 3 段逻辑 。
一是如果存在对应的事务日志 , 且事务状态是 ABORTED , 那就执行 tryRollback 方法回滚 , 该方法主要执行了 XA ROLLBACK {xid 。
二是如果事务状态是 SUCCESS , 那就执行 tryCommitTSO 方法回滚 , 该方法会设置 commit 时间戳 , 然后执行 XA COMMIT {xid 。
三是如果没找到事务日志 , 此时一般有两种可能:1)事务提交失败了 , 且没写下事务日志 , 此时需要回滚;2)事务还在两阶段提交的流程中 , 只是 prepare 较慢 , 还没开始写事务日志 , 此时不需要做任何操作 。 在 MySQL 中 , 如果发起 XA START 的连接没有关闭 , 其他连接是无法通过 XA ROLLBACK 或 XA COMMIT 来回滚或提交这个分支事务的 。 我们利用这一特性 , 首先在事务日志插入一条 ABORTED 记录 , 表明这个分布式事务需要回滚 , 然后尝试执行 XA ROLLBACK 回滚这个分支事务 。 如果遇到 2)的情况 , 则会收到特定的报错 , 此时再回滚掉插入 ABORTED 事务日志的操作 。 在我们插入 ABORTED 事务日志后 , 原本正在提交事务的线程在插入 SUCCESS 事务日志时会被阻塞 , 而在我们回滚掉插入 ABORTED 事务日志的操作后 , 事务提交流程就会继续进行下去 。
相关经验推荐
- 空调|一步棋走错!苹果iPhone就有可能被停止销售
- ios15|苹果iOS 15.6正式版体验:4个核心修复,但续航未提升?
- 苹果|苹果官方降价的原因找到了,它也面临销量下滑乃至出现库存问题
- 华为|华为新款笔记本外观、价格对标苹果MAC系列,你会选谁?
- 苹果|今年下半年多款折叠屏手机即将发布,这两家值得期待
- 苹果|手机市场基本没翻过车的四个系列:新品或值得闭眼入
- 一加科技|下半年最值得期待的三大旗舰机,华为苹果一加Ace Pro纷纷上榜
- 芯片|在苹果的压力下,高通也无法挽救国产手机,芯片研发终于被重视
- 华为mate|花粉福利!鸿蒙3.0或与华为Mate 50系列同时到来,9月直接对刚苹果
- 苹果|苹果再次“煽动”中企外迁,这个问题再也压不住了
