组成 , 这里的 gtrid 是“drds-事务 id @主分片Uid” , 这样保证了同一个事务在不同分片上执行 XA START , 会使用相同的 gtrid 。 bqual 设置当前连接的分片 , 用于在事务恢复时确定分支事务所在的分片 。在我们的例子中 , xid 为:'drds-13e101d74e400000@5ae6c3b5be613cd1' 'DB1_000001_GROUP' , 其中 13e101d74e400000 是事务 id , 5ae6c3b5be613cd1 是分片 1 的 Uid , DB1_000001_GROUP 是分片 1 的具体分片名称 。 值得注意的是 , 这里会把之前的时间戳发送到分片 1 。 至此 , 连接上的一些初始化操作已经完成 , 可以向执行器返回并执行写分片 1 的物理 SQL 。
COMMIT
最后 , 执行 COMMIT 提交事务 。 处理 COMMIT 的代码入口为 ServerConnection#commit() , 其主要调用了 TConnection#commit 方法 , 代码如下:
// TConnection#commit()public void commit() throws SQLException { try { // 触发事务的提交流程 。this.trx.commit();finally { // 大部分情况下 , 如果事务提交成功 , 或出现异常后正确处理了 ,// 所有连接会被关闭并释放 , trx.close() 相当于什么也不做 。// 但如果事务提交失败且没有处理 , 这里会回滚事务并释放所有物理连接 。this.trx.close(); // 去掉对这个事务对象的引用 , 意味着当前连接里这个事务一生的结束 。this.trx = null;我们重点关注一下事务的提交流程 , 上述 this.trx.commit() 调用时 , 会调用 ShareReadViewTransaction(该类继承了 AbstractTransaction , 基于 XA 事务实现了共享 readview 的功能 , XATransaction 和 TsoTransaction 都会继承这个类 , 在这里可以简单理解为 XA 事务的基类)的 commit 方法 , 代码如下:
// ShareReadViewTransaction#commit()public void commit() { if (!isCrossGroup) { // 如果只涉及了单个分片的写 , 进行一阶段提交优化 。commitOneShardTrx();else { // 正常的多分片分布式事务提交流程 。commitMultiShardTrx();在我们的例子中 , 由于只写了分片 1 , 所以进入一阶段提交优化 , 代码如下:
// ShareReadViewTransaction#commitOneShardTrx()protected void commitOneShardTrx() { // 对持有的所有物理连接执行以下流程 , 以提交每个物理连接开启的事务 。forEachHeldConnection((group conn participated) -{ if (!participated) { // 只读连接是 BEGIN 开启事务的 , 且只有读操作 , 执行 ROLLBACK 即可 。conn.execute(\"ROLLBACK\");else { // 获取 xid 。String xid = getXid(group); // 写连接是 XA START 开启事务的 , 执行 XA END 和 XA COMMIT ONE PHASE 提交事务 。conn.execute(\"XA END \" + xid + \"; XA COMMIT \" + xid + \" ONE PHASE\");); // 所有连接都提交了分支事务 , 释放并清空这些物理连接 。connectionHolder.closeAllConnections(); 我们一共持有了 2 个物理连接 。 对于分片 0 的只读连接 , 会直接执行 ROLLBACK;对于分片 1 的写连接 , 则执行 XA END 和 XA COMMIT ONE PHASE 提交事务 。 注意到我们并没有获取 commit timestamp , 因为在一阶段提交优化里 , commit timestamp 会由 InnoDB 计算生成:
具体的计算规则是:COMMIT_TS = MAX_SEQUENCE + 1 , 其中 MAX_SEQUENCE 为 InnoDB 本地维护的历史最大的 snapshot_ts 。
如果提交失败了 , 会调用事务的 close 方法 , 代码如下:
// AbstractTransaction#close()public void close() { // 回滚所有物理连接上的事务 。cleanupAllConnections(); // 释放并清空这些物理连接 。connectionHolder.closeAllConnections(); 值得一提的是 , cleanupAllConnections() 也是 ROLLBACK 语句主要调用的方法 。 因此为了同时了解 ROLLBACK 语句执行流程 , 我们也看一下 cleanupAllConnections 方法的代码:
// AbstractTransaction#cleanupAllConnections()protected final void cleanupAllConnections() { // 对持有的所有物理连接执行以下流程 , 以回滚每个物理连接开启的事务 。forEachHeldConnection((group conn participated) -{ if (conn.isClosed()) { return;if (!participated) { // 只读连接是 BEGIN 开启事务的 , 且只有读操作 , 执行 ROLLBACK 即可 。conn.execute(\"ROLLBACK\");else { // 获取 xid 。String xid = getXid(group); // 写连接是 XA START 开启事务的 , 执行 XA END 和 XA ROLLBACK 回滚事务 。conn.execute(\"XA END \" + xid + \"; XA ROLLBACK \" + xid);); 可以看到 , 回滚的逻辑是看情况执行 ROLLBACK 或 XA ROLLBACK 来回滚事务的 。
相关经验推荐
- 空调|一步棋走错!苹果iPhone就有可能被停止销售
- ios15|苹果iOS 15.6正式版体验:4个核心修复,但续航未提升?
- 苹果|苹果官方降价的原因找到了,它也面临销量下滑乃至出现库存问题
- 华为|华为新款笔记本外观、价格对标苹果MAC系列,你会选谁?
- 苹果|今年下半年多款折叠屏手机即将发布,这两家值得期待
- 苹果|手机市场基本没翻过车的四个系列:新品或值得闭眼入
- 一加科技|下半年最值得期待的三大旗舰机,华为苹果一加Ace Pro纷纷上榜
- 芯片|在苹果的压力下,高通也无法挽救国产手机,芯片研发终于被重视
- 华为mate|花粉福利!鸿蒙3.0或与华为Mate 50系列同时到来,9月直接对刚苹果
- 苹果|苹果再次“煽动”中企外迁,这个问题再也压不住了
