苹果公司|PolarDB-X 源码解读:事务的一生

苹果公司|PolarDB-X 源码解读:事务的一生

概述 本文将主要解读 PolarDB-X 中事务部分的相关代码 , 着重解读事务的一生在计算节点(CN)中的关键代码:从开始、执行、到最后提交这一整个生命周期 。
在阅读本文前 , 强烈推荐先阅读与 PolarDB-X 事务系统相关的文章:
PolarDB-X 强一致分布式事务原理 PolarDB-X 分布式事务的实现(一) PolarDB-X 分布式事务的实现(二)InnoDB CTS 扩展 PolarDB-X 分布式事务的实现(四):跨地域事务 无处不在的 MySQL XA 事务 以及此前发布的 PolarDB-X SQL 的一生
事务与连接 在 PolarDB-X 的 CN 层 , 与事务关系密切的是连接 。 这是因为数据节点(DN)也具备单个 DN 内的事务能力 , CN 则通过与 DN 的连接来管理 DN 上的事务 , 从而实现强一致的分布式事务能力 。 其中涉及到的连接大致如下图所示:

先简单说一下这里面涉及的一些连接 。 ServerConnection 类似于前端连接 , 大部分的 SQL 语句执行的入口都是 ServerConnection#innerExecute 。 TConnection 中的 executeSQL 方法负责 SQL 语句的真正执行 , 也负责创建新的事务对象 。 TConnection 会一直引用着这个事务对象 , 直到事务提交或回滚 。 事务对象里有一个 TransactionConnectionHolder , 负责管理该事务用到的所有物理连接(CN 连接 DN 的私有协议连接) 。 值得一提的是 , ExecutionContext 作为一条逻辑 SQL 执行的上下文 , 也会引用这个事务对象 。 这样 , 后续执行器需要使用物理连接与 DN 通信时 , 就可以通过 ExecutionContext 拿到事务对象 , 再通过事务对象的 TransactionConnectionHolder 拿到合适的物理连接 。
以上的各种连接 , 都会在下文继续讨论 。
两个例子 接下来 , 我们以两个简单的例子 , 来说明事务的一生在 CN 的代码中是如何体现的 。
测试用表:
CREATE TABLE `tb1` ( `id` int PRIMARY KEY `a` int) DBPARTITION BY HASH(`id`) 先在里面插入几条数据:
INSERT INTO tb1 VALUES (0 0) (1 1) (2 2) (3 3); 测试使用的两个例子:
-- Example 1: BEGIN; SELECT * FROM tb1 WHERE id = 0; UPDATE tb1 SET a = 100 WHERE id = 1;COMMIT;-- Example 2: BEGIN; SELECT * FROM tb1 WHERE id = 0; UPDATE tb1 SET a = 101 WHERE id = 1;UPDATE tb1 SET a = 101 WHERE id = 0;COMMIT; 注意到例 2 只比 例 1 多修改了 id = 1 的数据 。 测试表是按 id 拆分的 , 因此 id = 0 和 id = 1 的记录会落在不同的物理分片上(假设分别为分片 0 和分片 1) 。 例 1 读了分片 0 , 写了分片 1 , 然后提交了事务 , 这将会触发我们对单分片写的“一阶段提交优化” 。 例 2 读了分片 0 , 随后写了分片 1 和 分片 0 , 然后提交了事务 , 这将会进行完整的分布式事务提交流程 。 这两个例子还会触发“只读连接优化” , 即只有在第一次写的时候才真正开启分布式事务 。
在接下来的讨论中 , 我们默认使用 TSO 事务策略和 RR 的隔离级别 。
例 1 事务的一生 BEGIN
与 MySQL 类似 , 要开启一个事务 , 一般有两种方式 。 第一种方式是显式地执行 BEGIN 或 START TRANSACTION [transaction_characteristic
, 执行这两种语句 , 会调用 ServerConnection 中的 begin(boolean IsolationLevel) 方法 。 第二种方式是执行 SET autocommit = 0 , 当前 session 会隐式开启事务 , 这种方式会调用 ServerConnection 中的 setAutocommit(boolean boolean) 方法 。 两种方式都会调用 TConnection 的 setAutoCommit 方法 。 这些方法都只是简单地记录了一些变量(比如 transaction_characteristic 中设定的事务相关变量) , 同时标记这个连接开启了事务 。 此时 , 事务对象也还没创建出来 , 也没有与后端连接进行任何交互 。

相关经验推荐