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


通过连接管理器拿物理连接的代码 TransactionConnectionHolder#getConnection 如下:
// TransactionConnectionHolder#getConnection(String String IDataSource RW)public IConnection getConnection(String schema String group IDataSource ds RW rw) { // 尝试获取该分片上的写连接 , 如果有 , 直接返回这个写连接 。HeldConnection groupWriteConn = groupHeldWriteConn.get(group); if (groupWriteConn != null) { return groupWriteConn.connection;HeldConnection freeReadConn = /* 尝试找到读连接 */; if (freeReadConn != null) { if (/* 当前需要写连接 */) { // 设置当前连接为写连接 , participated = true 意味着该连接是写连接 。freeReadConn.participated = true; this.groupHeldWriteConn.put(group freeReadConn); // 由于原本是读连接 , 这里才真正开启分布式事务 。this.trx.commitNonParticipant(group freeReadConn.connection); this.trx.begin(schema freeReadConn.group freeReadConn.connection);return freeReadConn.connection;// 当前分片没有任何连接 , 创建一个新的连接 。 还会根据读写类型 ,// 设置好写连接 groupHeldWriteConn 或读连接集合 groupHeldReadConns 。IConnection conn = new DeferredConnection(/* 这里会获取并封装该分片的私有协议连接 */); if (/* 需要写连接 */) { // 开启正常的分布式事务 。this.trx.begin(schema group conn);else { // 优化为只读事务 。this.trx.beginNonParticipant(group conn);return conn; 在我们的例子中 , 由于是第一条语句 , 该分片上还没有任何连接 , 因此会先生成一个连接该分片的私有协议连接 , 包装成 DeferredConnection , 然后因为是读请求 , 会调用 beginNonParticipant。
TsoTransaction 的 beginNonParticipant 方法如下:
// TsoTransaction#beginNonParticipant(String IConnection)protected void beginNonParticipant(String group IConnection conn) throws SQLException { if (snapshotTimestamp0) { // 该事务从未拿过时间戳 , 则在这里获取 。snapshotTimestamp = nextTimestamp();// 使用私有协议的流水线执行机制执行 BEGIN 。conn.executeLater(\"BEGIN\"); // 在 BEGIN 后发送时间戳 。sendSnapshotSeq(conn); 在我们的例子中 , 私有协议连接会流水线执行 BEGIN 语句(非阻塞 , 不等结果返回) , 且在稍后执行物理 SQL 时才发送时间戳 。 至此 , 连接上的一些初始化操作已经完成 , 可以向执行器返回并执行读分片 0 的物理 SQL 。
写分片 1
随后 , 我们执行 UPDATE tb1 SET a = 100 WHERE id = 1 。 在执行器阶段 , 需要给分片 1 下发一条 UPDATE 语句 , 此时需要获取分片 1 的物理连接 , 因此又会调用 AbstractTransaction#getConnection 方法 , 通过事务对象拿物理连接 。 通过前面贴出的代码 , 我们发现由于是事务的第一个写请求 , 因此分片 1 会视作主分片 , 用于生成 xid 和稍后记录事务日志 。
在获取物理连接时 , 又会调用 TransactionConnectionHolder#getConnection 方法 , 通过连接管理器拿物理连接 。 通过前面贴出的代码 , 我们发现由于分片 1 没有任何连接 , 因此会生成一个私有协议连接 , 包装成 DeferredConnection 。 与读分片 0 不同 , 由于是写请求 , 会执行 TsoTransaction 的 begin 方法 。
TsoTransaction 的 begin 方法如下:
// TsoTransaction#begin(String String IConnection)protected void begin(String schema String group IConnection conn) throws SQLException { if (snapshotTimestamp0) { // 该事务从未拿过时间戳 , 则在这里获取 。snapshotTimestamp = nextTimestamp();// 获取 xid 。String xid = getXid(group); // 触发私有协议流水线执行 XA START 。conn.executeLater(\"XA START \" + xid); // 在 XA START 后发送时间戳 。sendSnapshotSeq(conn); 简单来说 , 和此前 beginNonParticipant 方法唯一的区别在于 , 使用了 XA START 开启事务 。 根据 MySQL 关于 XA 事务的说明 , xid 由 gtrid [ bqual [ formatID

相关经验推荐