Postgresql源码(73)两阶段事务PrepareTransaction事务如何与会话解绑(上)
2023-06-13 09:11:01 时间
相关 《Postgresql源码(69)常规锁简单分析》 《Postgresql源码(73)两阶段事务PrepareTransaction事务如何与会话解绑(上)》 《Postgresql源码(74)两阶段事务PrepareTransaction事务如何与会话解绑(下)》
总结速查:
- PrepareTransaction类似于事务提交过程,因为事务提交也会将事务状态与会话解绑、做清理工作。
- 不同的是PrepareTransaction后面还要恢复信息以便二次提交,所以PrepareTransaction会保存提交所需的信息,并且将与会话关联的锁解绑,最后清理事务相关资源,达到事务与会话解绑的效果。
- 注意虽然解绑了,锁还在,只是锁与任何会话都没关系了。
1 背景
两阶段事务提供的核心能力:一阶段提交的事务保证在二阶段提交时,可以正常提交。即使一阶段提交后,数据库宕机重启,都不会影响二阶段提交。这是普通事务不具备的能力,也是分布式事务全局提交依赖的功能。
两阶段语法例子:
create table tbl01(i int primary key);
insert into tbl01 values (1),(2),(3);
begin;
update tbl01 set i = 4 where i = 3;
prepare transaction 'x1';
select locktype,database,relation,transactionid,virtualtransaction,pid,mode,granted,fastpath,waitstart from pg_locks where pid is null;
locktype | database | relation | transactionid | virtualtransaction | pid | mode | granted | fastpath | waitstart
---------------+----------+----------+---------------+--------------------+-----+------------------+---------+----------+-----------
transactionid | | | 4003120 | 4/7843 | | ExclusiveLock | t | f |
relation | 19525 | 19598 | | 4/7843 | | RowExclusiveLock | t | f |
relation | 19525 | 19601 | | 4/7843 | | RowExclusiveLock | t | f |
commit prepared 'x1';
select locktype,database,relation,transactionid,virtualtransaction,pid,mode,granted,fastpath,waitstart from pg_locks where pid is null;
(0 rows)
- 执行完PREPARE后,事务会和当前会话“解绑”,当前会话结束事务状态,可以再起其他事务。
- 事务信息会持久化到磁盘上,如果服务器发生宕机,在启动后,也可以正常提交两阶段事务。
- 注意:锁还在(两把常规锁分别加在表和索引上,一把事务ID锁)。在上面锁表中看到
PID is null
的就是两阶段留下的锁。 - 注意:锁虽然还在,但是已经和会话无关联关系,变成了”野锁“。
2 prepare transaction
prepare transaction执行完成后,预期内要完成的事情:
- 恢复事务块状态到default初始模式。
- 保存所有使用过的、事务提交时需要的资源。
- 将保存的资源持久化:写两阶段文件
pg_twophase/xxx
和wal日志pg_wal/xxx
。
prepare transaction命令和其他事务控制语句类似:在DDL执行中调整状态,在最后finish_xact_command->CommitTransactionCommand
时调用功能函数干活:PrepareTransaction。
2.1 数据结构
- 整体结构:TwoPhaseStateData头 + max_prepared_xacts个指针 + max_prepared_xacts个两阶段状态结构
- 头部freeGXacts连接所有空闲GlobalTransactionData
- 头部prepXacts柔性数组便于找到GlobalTransactionData指针
- 每开启一个两阶段事务就占用一个GlobalTransactionData指针、一个GlobalTransactionData结构
2.2 PrepareTransaction分段分析
static void
PrepareTransaction(void)
{
...
事务提交准备:
CallXactCallbacks(XACT_EVENT_PRE_PREPARE);
AfterTriggerEndXact(true);
PreCommit_on_commit_actions();
smgrDoPendingSyncs(true, false);
AtEOXact_LargeObject(true);
PreCommit_CheckForSerializationFailure();
停止相应中断
HOLD_INTERRUPTS();
事务状态转换,记录时间点
s->state = TRANS_PREPARE;
prepared_at = GetCurrentTimestamp();
构造GlobalTransactionData:MarkAsPreparing
- 加锁:TwoPhaseStateLock
- freelist中取一个Data:
gxact = TwoPhaseState->prepXacts[i];
- 构造Data、补全MYPROC、MyLockedGxact指向当前Data:
MarkAsPreparingGuts
- 现在肯定还没落盘:
gxact->ondisk = false;
- 放锁:TwoPhaseStateLock
gxact = MarkAsPreparing(xid, prepareGID, prepared_at,
GetUserId(), MyDatabaseId);
prepareGID = NULL;
收集需要保存的信息:
- StartPrepare
- AtPrepare_Notify
- AtPrepare_Locks:收集锁信息
- AtPrepare_PredicateLocks
- AtPrepare_PgStat
- AtPrepare_MultiXact
- AtPrepare_RelationMap
StartPrepare(gxact);
AtPrepare_Notify();
AtPrepare_Locks();
AtPrepare_PredicateLocks();
AtPrepare_PgStat();
AtPrepare_MultiXact();
AtPrepare_RelationMap();
上面所有收集到的信息写入XLOG。
EndPrepare(gxact);
注意PostPrepare_Locks这一步非常重要:一阶段提交后,事务锁状态信息和会话也会解耦,就是这个函数做的。
PostPrepare_Locks将锁信息和PGPROC proc关联,清理LOCALLOCK锁表上的关联关系。
效果就是一阶段提交后,锁和会话的关联关系没了,锁变成了”野锁“。
PostPrepare_Locks(xid);
开始事务结束的标准清理操作:
ProcArrayClearTransaction(MyProc);
CallXactCallbacks(XACT_EVENT_PREPARE);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_BEFORE_LOCKS,
true, true);
AtEOXact_Buffers(true);
AtEOXact_RelationCache(true);
PostPrepare_PgStat();
PostPrepare_Inval();
PostPrepare_smgr();
PostPrepare_MultiXact(xid);
PostPrepare_PredicateLocks(xid);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_LOCKS,
true, true);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_AFTER_LOCKS,
true, true);
PostPrepare_Twophase();
AtEOXact_GUC(true, 1);
AtEOXact_SPI(true);
AtEOXact_Enum();
AtEOXact_on_commit_actions(true);
AtEOXact_Namespace(true, false);
AtEOXact_SMgr();
AtEOXact_Files(true);
AtEOXact_ComboCid();
AtEOXact_HashTables(true);
/* don't call AtEOXact_PgStat here; we fixed pgstat state above */
AtEOXact_Snapshot(true, true);
pgstat_report_xact_timestamp(0);
CurrentResourceOwner = NULL;
ResourceOwnerDelete(TopTransactionResourceOwner);
s->curTransactionOwner = NULL;
CurTransactionResourceOwner = NULL;
TopTransactionResourceOwner = NULL;
AtCommit_Memory();
s->fullTransactionId = InvalidFullTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->childXids = NULL;
s->nChildXids = 0;
s->maxChildXids = 0;
XactTopFullTransactionId = InvalidFullTransactionId;
nParallelCurrentXids = 0;
事务状态切回原始状态,会话与事务完成解绑
s->state = TRANS_DEFAULT;
RESUME_INTERRUPTS();
}
相关文章
- Postgresql源码(77)plpgsql中参数传递和赋值
- Postgresql源码(78)plpgsql中调用call proc()时的参数传递和赋值(pl参数)
- Postgresql源码(79)plpgsql中多层调用时参数传递关键点分析(pl参数)
- Mac配置postgresql容器并连接
- Postgresql源码(84)语义分析——函数调用结构CallStmt的构造与函数多态的实现(pl参数)
- Postgresql源码(93)Postgresql函数内事务控制实现原理(附带Oracle对比)
- Postgresql源码(98)lex与yacc的定制交互方式
- Windows 系统 PostgreSQL 手工安装配置方法
- postgresql 实现得到时间对应周的周一案例
- Postgresql排序与limit组合场景性能极限优化详解
- PostgreSQL 25005: no_active_sql_transaction_for_branch_transaction 报错 故障修复 远程处理
- postgresql 物理备份 tar + pigz详解程序员
- PostgreSQL语法结构详解数据库
- 优化实现PostgreSQL缓存优化,更高性能!(postgresql缓存)
- 索引索引PostgreSQL数组:极大提高查询性能.(postgresql数组)
- 创建PostgreSQL数据库:一步一步操作指南(postgresql创建数据库)
- PostgreSQL快速导入SQL文件(postgresql导入sql文件)
- 安装及使用PostgreSQL数据库安装与应用指南(postgresql数据库)
- PostgreSQL库:高可靠性企业级数据库的首选(postgresql库)
- 数据库简洁高效:易飞搭建PostgreSQL数据库方案(易飞postgresql)
- 大数据应用探索PostgreSQL在中国大数据应用中的可能性(postgresql中国)
- 利用 PostgreSQL 脚本加速数据库操作!(postgresql脚本)
- PostgreSQL外键:更好的数据库关联性(postgresql外键)
- 分析PostgreSQL源码深度剖析(postgresql源码)
- Postgresql数据库实现事务回滚技术(postgresql回滚)
- 新手快速登录Postgresql数据库(登录postgresql)
- 深入了解 PostgreSQL 数据库结构(postgresql结构)
- 官方文档PostgreSQL:正式中文文档(postgresql中文)