前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Postgresql源码(71)子事务数据结构与DDL

Postgresql源码(71)子事务数据结构与DDL

作者头像
mingjie
发布2022-11-30 16:11:38
3420
发布2022-11-30 16:11:38
举报
文章被收录于专栏:Postgresql源码分析

相关 子事务的可见性判断、性能问题请看这篇:《Postgresql源码(25)子事务可见性判断和性能问题》 子事务的DDL和数据结构请看这篇:《Postgresql源码(71)子事务数据结构与DDL分析》

1 SAVEPOINT、ROLLBACK执行细节

测试用例

代码语言:javascript
复制
drop table table1;
create table table1(i int);

BEGIN;
    INSERT INTO table1 VALUES (1);
    -- s1
    SAVEPOINT my_savepoint1;
    INSERT INTO table1 VALUES (2);
    
    -- s2
    SAVEPOINT my_savepoint2;
    INSERT INTO table1 VALUES (3);

    -- rollback to s2
    ROLLBACK TO SAVEPOINT my_savepoint2;
    SELECT * FROM table1;               -- shows rows 1 and 2
    
    -- release s2
    RELEASE SAVEPOINT my_savepoint2;

    -- rollback to s1
    ROLLBACK TO SAVEPOINT my_savepoint1;
    SELECT * FROM table1;               -- shows only row 1
COMMIT;

1.1 执行SAVEPOINT my_savepoint;

在执行时,由ProcessUtility模块执行DefineSavepoint完成DDL(因为是第一次申请,所以申请时的事务内存上下文是TopTransactionContext)完成后走到顶层事务处理模块,走finish_xact_command->CommitTransactionCommand完成事务状态转移和收尾。

  • 【DDL】:部分执行PushTransaction函数,将当前CurrentTransactionState切换为新建的子事务TransactionState。
    • nestingLevel加一;
    • 事务块流转状态变为TBLOCK_SUBBEGIN;
  • 【CommitTransactionCommand】:完成收尾工作,在顶层内存事务内存上下文申请新的CurTransactionContext,记录在CurrentTransactionState->curTransactionContext和全局变量CurTransactionContext中。

1.2 执行ROLLBACK TO SAVEPOINT my_savepoint;

  • 【DDL】:标记事务块状态。
  • 【CommitTransactionCommand】:清理资源、标记子事务ID abort、弹出父事务、重新拉起一个同名子事务。

2 子事务ID分配

流程

  1. 事务ID都是在写发生之前申请的,savepoint语句并不会产生事务ID。
  2. 子事务ID和事务ID使用一套分配机制,区别是申请完了记录的位置不同:
  3. 普通事务ID只有一个记录在PGPROC->xid中。
  4. 子事务ID可能有多个(申请多个检查点),多个值记录在PGPROC->subxids数组中,同时每个PGPROC维护一个subxidStates,记录有多少个子事务、子事务数量是不是已经超了(最多存64个),参考下面第二张图。
  5. 注意SubTransSetParent保存的内容:用xid查找自己的父事务ID。参考这一篇《Postgresql源码(25)子事务可见性判断和性能问题》

子事务相关数据结构:

3 子事务pg_subtrans

总结:通过xid找到parenet xid的slru数据结构,之前的很多文章提到过。参考这一篇《Postgresql源码(25)子事务可见性判断和性能问题》

4 子事务的两阶段提交

涉及子事务的事务提交时,需要把涉及到的所有子事务全部提交掉。

按照TransactionIdSetTreeStatus函数的逻辑,如果子事务状态和顶层事务全部在一个CLOG页面,那么拿一个CLOG锁就可以搞定了。

但是如果子事务状态 和 父事务不在一个CLOG页面上,那么由于每次还是只拿一个页面的锁,操作就变成了两阶段完成,第一阶段把所有子事务改成TRANSACTION_STATUS_SUB_COMMITTED。当所有子事务都为TRANSACTION_STATUS_SUB_COMMITTED后,在修改父事务状态为TRANSACTION_STATUS_COMMITTED,然后再把子事务的状态配置为TRANSACTION_STATUS_COMMITTED。

下面是官方案例:

代码语言:javascript
复制
 * Example:
 *		TransactionId t commits and has subxids t1, t2, t3, t4
 *		t is on page p1, t1 is also on p1, t2 and t3 are on p2, t4 is on p3
 *		1. update pages2-3:
 *					page2: set t2,t3 as sub-committed
 *					page3: set t4 as sub-committed
 *		2. update page1:
 *					set t1 as sub-committed,
 *					then set t as committed,
					then set t1 as committed
 *		3. update pages2-3:
 *					page2: set t2,t3 as committed
 *					page3: set t4 as committed
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 SAVEPOINT、ROLLBACK执行细节
    • 1.1 执行SAVEPOINT my_savepoint;
      • 1.2 执行ROLLBACK TO SAVEPOINT my_savepoint;
      • 2 子事务ID分配
      • 3 子事务pg_subtrans
      • 4 子事务的两阶段提交
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档