PostgreSQL中函数CommitTransaction的实现逻辑是什么
这篇文章主要介绍“PostgreSQL中函数CommitTransaction的实现逻辑是什么”,在日常操作中,相信很多人在PostgreSQL中函数CommitTransaction的实现逻辑是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中函数CommitTransaction的实现逻辑是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
一、数据结构TransactionState
事务状态结构体
/**transactionstates-transactionstatefromserverperspective*事务状态枚举-服务器视角的事务状态*/typedefenumTransState{TRANS_DEFAULT,/*idle空闲*/TRANS_START,/*transactionstarting事务启动*/TRANS_INPROGRESS,/*insideavalidtransaction进行中*/TRANS_COMMIT,/*commitinprogress提交中*/TRANS_ABORT,/*abortinprogress回滚中*/TRANS_PREPARE/*prepareinprogress准备中*/}TransState;/**transactionblockstates-transactionstateofclientqueries*事务块状态-客户端查询的事务状态**Note:thesubtransactionstatesareusedonlyfornon-topmost*transactions;theothersappearonlyinthetopmosttransaction.*注意:subtransaction只用于非顶层事务;其他字段用于顶层事务.*/typedefenumTBlockState{/*not-in-transaction-blockstates未进入事务块状态*/TBLOCK_DEFAULT,/*idle空闲*/TBLOCK_STARTED,/*runningsingle-querytransaction单个查询事务*//*transactionblockstates事务块状态*/TBLOCK_BEGIN,/*startingtransactionblock开始事务块*/TBLOCK_INPROGRESS,/*livetransaction进行中*/TBLOCK_IMPLICIT_INPROGRESS,/*livetransactionafterimplicitBEGIN隐式事务,进行中*/TBLOCK_PARALLEL_INPROGRESS,/*livetransactioninsideparallelworker并行worker中的事务,进行中*/TBLOCK_END,/*COMMITreceived接收到COMMIT*/TBLOCK_ABORT,/*failedxact,awaitingROLLBACK失败,等待ROLLBACK*/TBLOCK_ABORT_END,/*failedxact,ROLLBACKreceived失败,已接收ROLLBACK*/TBLOCK_ABORT_PENDING,/*livexact,ROLLBACKreceived进行中,接收到ROLLBACK*/TBLOCK_PREPARE,/*livexact,PREPAREreceived进行中,接收到PREPARE*//*subtransactionstates子事务状态*/TBLOCK_SUBBEGIN,/*startingasubtransaction开启*/TBLOCK_SUBINPROGRESS,/*livesubtransaction进行中*/TBLOCK_SUBRELEASE,/*RELEASEreceived接收到RELEASE*/TBLOCK_SUBCOMMIT,/*COMMITreceivedwhileTBLOCK_SUBINPROGRESS进行中,接收到COMMIT*/TBLOCK_SUBABORT,/*failedsubxact,awaitingROLLBACK失败,等待ROLLBACK*/TBLOCK_SUBABORT_END,/*failedsubxact,ROLLBACKreceived失败,已接收ROLLBACK*/TBLOCK_SUBABORT_PENDING,/*livesubxact,ROLLBACKreceived进行中,接收到ROLLBACK*/TBLOCK_SUBRESTART,/*livesubxact,ROLLBACKTOreceived进行中,接收到ROLLBACKTO*/TBLOCK_SUBABORT_RESTART/*failedsubxact,ROLLBACKTOreceived失败,已接收ROLLBACKTO*/}TBlockState;/**transactionstatestructure*事务状态结构体*/typedefstructTransactionStateData{//事务IDTransactionIdtransactionId;/*myXID,orInvalidifnone*///子事务IDSubTransactionIdsubTransactionId;/*mysubxactID*///保存点名称char*name;/*savepointname,ifany*///保存点级别intsavepointLevel;/*savepointlevel*///低级别的事务状态TransStatestate;/*low-levelstate*///高级别的事务状态TBlockStateblockState;/*high-levelstate*///事务嵌套深度intnestingLevel;/*transactionnestingdepth*///GUC上下文嵌套深度intgucNestLevel;/*GUCcontextnestingdepth*///事务生命周期上下文MemoryContextcurTransactionContext;/*myxact-lifetimecontext*///查询资源ResourceOwnercurTransactionOwner;/*myqueryresources*///按XID顺序保存的已提交的子事务IDTransactionId*childXids;/*subcommittedchildXIDs,inXIDorder*///childXids数组大小intnChildXids;/*#ofsubcommittedchildXIDs*///分配的childXids数组空间intmaxChildXids;/*allocatedsizeofchildXids[]*///上一个CurrentUserIdOidprevUser;/*previousCurrentUserIdsetting*///上一个SecurityRestrictionContextintprevSecContext;/*previousSecurityRestrictionContext*///上一事务是否只读?boolprevXactReadOnly;/*entry-timexactr/ostate*///是否处于Recovery?boolstartedInRecovery;/*didwestartinrecovery?*///XID是否已保存在WALRecord中?booldidLogXid;/*hasxidbeenincludedinWALrecord?*///Enter/ExitParallelMode计数器intparallelModeLevel;/*Enter/ExitParallelModecounter*///父事务状态structTransactionStateData*parent;/*backlinktoparent*/}TransactionStateData;//结构体指针typedefTransactionStateData*TransactionState;二、源码解读
CommitTransaction函数,提交事务,并执行相关的清理操作.
/**CommitTransaction**NB:ifyouchangethisroutine,betterlookatPrepareTransactiontoo!*注意:如果改变了这个过程的逻辑,最好同时处理PrepareTransaction.*/staticvoidCommitTransaction(void){TransactionStates=CurrentTransactionState;TransactionIdlatestXid;boolis_parallel_worker;is_parallel_worker=(s->blockState==TBLOCK_PARALLEL_INPROGRESS);/*Enforceparallelmoderestrictionsduringparallelworkercommit.*///如为并行worker,强制进入并行模式if(is_parallel_worker)EnterParallelMode();ShowTransactionState("CommitTransaction");/**checkthecurrenttransactionstate*检查当前事务状态*/if(s->state!=TRANS_INPROGRESS)elog(WARNING,"CommitTransactionwhilein%sstate",TransStateAsString(s->state));Assert(s->parent==NULL);/**Dopre-commitprocessingthatinvolvescallinguser-definedcode,such*astriggers.Sinceclosingcursorscouldqueuetriggeractions,*triggerscouldopencursors,etc,wehavetokeeploopinguntilthere's*nothinglefttodo.*执行涉及调用用户定义代码(如触发器)的预提交处理。*因为关闭游标可能会执行触发器,触发器可能打开游标,等等,*所以我们必须一直循环,直到没有什么可做的。*/for(;;){/**Fireallcurrentlypendingdeferredtriggers.*触发所有当前活动的触发器*/AfterTriggerFireDeferred();/**Closeopenportals(convertingholdableonesintostaticportals).*Ifthereweren'tany,wearedone...otherwiseloopbacktocheck*iftheyqueueddeferredtriggers.Lather,rinse,repeat.*关闭打开的portals(将可持有门户转换为静态门户).*如果已不存在,则说明已完成.*否则一直循环检查触发器队列.*/if(!PreCommit_Portals(false))break;}CallXactCallbacks(is_parallel_worker?XACT_EVENT_PARALLEL_PRE_COMMIT:XACT_EVENT_PRE_COMMIT);/**Theremainingactionscannotcallanyuser-definedcode,soit'ssafe*tostartshuttingdownwithin-transactionservices.Butnotethatmost*ofthisstuffcouldstillthrowanerror,whichwouldswitchusinto*thetransaction-abortpath.*其余的操作不能调用任何用户定义的代码,因此可以安全地开始关闭事务内的服务。*但是请注意,大多数这些动作仍然会抛出错误,这将把执行流程切换到事务中止执行路径上。*//*Ifwemighthaveparallelworkers,cleanthemupnow.*///存在并行worker,清除之if(IsInParallelMode())AtEOXact_Parallel(true);/*Shutdownthedeferred-triggermanager*///关闭延迟触发器管理器AfterTriggerEndXact(true);/**LetONCOMMITmanagementdoitsthing(musthappenafterclosing*cursors,toavoiddangling-referenceproblems)*让ONCOMMIT管理器执行这个事情.*(必须在关闭游标后发生,以避免挂起引用问题)*/PreCommit_on_commit_actions();/*closelargeobjectsbeforelower-levelcleanup*///在低级别的清理请,关闭大对象AtEOXact_LargeObject(true);/**Markserializabletransactionascompleteforpredicatelocking*purposes.Thisshouldbedoneaslateaswecanputitandstillallow*errorstoberaisedforfailurepatternsfoundatcommit.*将可序列化事务标记为谓词锁定完成。*这应该尽可能迟地完成,并且仍然允许在提交时发现的失败从而引发错误。*/PreCommit_CheckForSerializationFailure();/**InsertnotificationssentbyNOTIFYcommandsintothequeue.This*shouldbelateinthepre-commitsequencetominimizetimespent*holdingthenotify-insertionlock.*将NOTIFY命令发送的通知插入到队列中。*这应该在预提交序列的末尾,以最小化持有通知插入锁的时间。*/PreCommit_Notify();/*Preventcancel/dieinterruptwhilecleaningup*///在清理时禁用中断HOLD_INTERRUPTS();/*Commitupdatestotherelationmap---dothisaslateaspossible*///提交更新到relationmap--尽可能晚的执行该动作AtEOXact_RelationMap(true,is_parallel_worker);/**setthecurrenttransactionstateinformationappropriatelyduring*commitprocessing*在commit过程中设置当前事务状态信息.*/s->state=TRANS_COMMIT;s->parallelModeLevel=0;if(!is_parallel_worker){/**WeneedtomarkourXIDsascommittedinpg_xact.Thisiswherewe*durablycommit.*我们需要在pg_xact中将xid标记为已提交。*/latestXid=RecordTransactionCommit();}else{/**WemustnotmarkourXIDcommitted;theparallelmasteris*responsibleforthat.*并行worker,不需要标记XID的提交标记,并行管理器处理此事情.*/latestXid=InvalidTransactionId;/**MakesurethemasterwillknowaboutanyWALwewrotebeforeit*commits.*确保master在提交之前知道worker写入的WAL。*/ParallelWorkerReportLastRecEnd(XactLastRecEnd);}TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);/**Letothersknowaboutnotransactioninprogressbyme.Notethatthis*mustbedone_before_releasinglocksweholdand_after_*RecordTransactionCommit.*通知其他进程知道该进程没有进行中的事务。*注意,这必须在释放持有的锁之前执行,并在RecordTransactionCommit之后执行。*/ProcArrayEndTransaction(MyProc,latestXid);/**Thisisallpost-commitcleanup.Notethatifanerrorisraisedhere,*it'stoolatetoabortthetransaction.Thisshouldbejust*noncriticalresourcereleasing.*这些都是提交后清理。*请注意,如果这里出现错误,则终止事务就太迟了.*这应该是非关键的资源释放。**Theorderingofoperationsisnotentirelyrandom.Theideais:*releaseresourcesvisibletootherbackends(eg,files,bufferpins);*thenreleaselocks;thenreleasebackend-localresources.Wewantto*releaselocksatthepointwhereanybackendwaitingforuswillsee*ourtransactionasbeingfullycleanedup.*操作的顺序并不是完全随机的。*其思想是:释放对其他后台进程可见的资源(如文件、bufferpins);*然后释放锁;然后释放后端本地资源。*我们希望在所有等待我们的后台进程看到我们的事务被完全清理的时候才释放锁。**Resourcesthatcanbeassociatedwithindividualqueriesarehandledby*theResourceOwnermechanism.Theothercallshereareforbackend-wide*state.*与单个查询关联的资源由ResourceOwner机制处理。*这里的其他调用是针对后台进程范围的状态的。*/CallXactCallbacks(is_parallel_worker?XACT_EVENT_PARALLEL_COMMIT:XACT_EVENT_COMMIT);ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_BEFORE_LOCKS,true,true);/*Checkwe'vereleasedallbufferpins*///检查已释放所有的bufferpinsAtEOXact_Buffers(true);/*Cleanuptherelationcache*///清理关系缓存AtEOXact_RelationCache(true);/**Makecatalogchangesvisibletoallbackends.Thishastohappenafter*relcachereferencesaredropped(seecommentsfor*AtEOXact_RelationCache),butbeforelocksarereleased(ifanyoneis*waitingforlockonarelationwe'vemodified,wewantthemtoknow*aboutthecatalogchangebeforetheystartusingtherelation).*使目录更改对所有后台进程可见。*这必须发生在relcache引用被删除之后(参见AtEOXact_RelationCache注释),*但是在锁被释放之前(如果有人在等待我们修改的关系的锁,我们希望他们在开始使用关系之前知道目录的更改)。*/AtEOXact_Inval(true);AtEOXact_MultiXact();ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_LOCKS,true,true);ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_AFTER_LOCKS,true,true);/**Likewise,droppingoffilesdeletedduringthetransactionisbestdone*afterreleasingrelcacheandbufferpins.(Thisisnotstrictly*necessaryduringcommit,sincesuchpinsshouldhavebeenreleased*already,butthisorderingisdefinitelycriticalduringabort.)Since*thismaytakemanyseconds,alsodelayuntilafterreleasinglocks.*Otherbackendswillobservetheattendantcatalogchangesandnot*attempttoaccessaffectedfiles.*同样,在事务期间删除的文件的清理最好在释放relcache和bufferpin之后进行。*(这在提交过程中并不是必须的,因为这样的pins应该已经被释放了,*但是该顺序在中止过程中绝对是至关重要的。)*因为这可能需要较长的时间,所以也要延迟到释放锁之后。*其他后台进程将监控相关的catalog更改,不尝试访问受影响的文件。*/smgrDoPendingDeletes(true);AtCommit_Notify();AtEOXact_GUC(true,1);AtEOXact_SPI(true);AtEOXact_Enum();AtEOXact_on_commit_actions(true);AtEOXact_Namespace(true,is_parallel_worker);AtEOXact_SMgr();AtEOXact_Files(true);AtEOXact_ComboCid();AtEOXact_HashTables(true);AtEOXact_PgStat(true);AtEOXact_Snapshot(true,false);AtEOXact_ApplyLauncher(true);pgstat_report_xact_timestamp(0);CurrentResourceOwner=NULL;ResourceOwnerDelete(TopTransactionResourceOwner);s->curTransactionOwner=NULL;CurTransactionResourceOwner=NULL;TopTransactionResourceOwner=NULL;AtCommit_Memory();s->transactionId=InvalidTransactionId;s->subTransactionId=InvalidSubTransactionId;s->nestingLevel=0;s->gucNestLevel=0;s->childXids=NULL;s->nChildXids=0;s->maxChildXids=0;XactTopTransactionId=InvalidTransactionId;nParallelCurrentXids=0;/**donewithcommitprocessing,setcurrenttransactionstatebackto*default*完成提交处理后,将当前事务状态设置为default*/s->state=TRANS_DEFAULT;RESUME_INTERRUPTS();}三、跟踪分析
插入数据,执行commit
10:57:56(xdb@[local]:5432)testdb=#begin;BEGIN10:57:59(xdb@[local]:5432)testdb=#*insertintot_session1values(1);INSERT0110:58:01(xdb@[local]:5432)testdb=#*commit;
启动gdb,设置断点
(gdb)bCommitTransactionBreakpoint1at0x5482ae:filexact.c,line1969.(gdb)cContinuing.Breakpoint1,CommitTransaction()atxact.c:19691969TransactionStates=CurrentTransactionState;(gdb)
查看调用栈
(gdb)bt#0CommitTransaction()atxact.c:1969#10x0000000000549078inCommitTransactionCommand()atxact.c:2831#20x00000000008c8ea9infinish_xact_command()atpostgres.c:2523#30x00000000008c6b5dinexec_simple_query(query_string=0x2c97ec8"commit;")atpostgres.c:1170#40x00000000008cae70inPostgresMain(argc=1,argv=0x2cc3dc8,dbname=0x2cc3c30"testdb",username=0x2c94ba8"xdb")atpostgres.c:4182#50x000000000082642binBackendRun(port=0x2cb9c00)atpostmaster.c:4361#60x0000000000825b8finBackendStartup(port=0x2cb9c00)atpostmaster.c:4033#70x0000000000821f1cinServerLoop()atpostmaster.c:1706#80x00000000008217b4inPostmasterMain(argc=1,argv=0x2c92b60)atpostmaster.c:1379#90x00000000007488efinmain(argc=1,argv=0x2c92b60)atmain.c:228(gdb)
当前事务信息
(gdb)p*s$1={transactionId=2410,subTransactionId=1,name=0x0,savepointLevel=0,state=TRANS_INPROGRESS,blockState=TBLOCK_END,nestingLevel=1,gucNestLevel=1,curTransactionContext=0x2d3cfa0,curTransactionOwner=0x2cc5868,childXids=0x0,nChildXids=0,maxChildXids=0,prevUser=10,prevSecContext=0,prevXactReadOnly=false,startedInRecovery=false,didLogXid=true,parallelModeLevel=0,parent=0x0}(gdb)
执行相关判断,执行预处理等
(gdb)n1976if(is_parallel_worker)(gdb)1979ShowTransactionState("CommitTransaction");(gdb)1984if(s->state!=TRANS_INPROGRESS)(gdb)1987Assert(s->parent==NULL);(gdb)2000AfterTriggerFireDeferred();(gdb)2007if(!PreCommit_Portals(false))(gdb)2008break;(gdb)2011CallXactCallbacks(is_parallel_worker?XACT_EVENT_PARALLEL_PRE_COMMIT(gdb)
继续执行预处理
(gdb)n2022if(IsInParallelMode())(gdb)2026AfterTriggerEndXact(true);(gdb)2032PreCommit_on_commit_actions();(gdb)2035AtEOXact_LargeObject(true);(gdb)(gdb)2042PreCommit_CheckForSerializationFailure();(gdb)2049PreCommit_Notify();(gdb)2052HOLD_INTERRUPTS();(gdb)2055AtEOXact_RelationMap(true);(gdb)
修改事务状态
2061s->state=TRANS_COMMIT;(gdb)2062s->parallelModeLevel=0;(gdb)
执行实际的提交事务操作
(gdb)2064if(!is_parallel_worker)(gdb)2070latestXid=RecordTransactionCommit();(gdb)2087TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);(gdb)
通知其他进程知道该进程没有进行中的事务。
(gdb)2094ProcArrayEndTransaction(MyProc,latestXid);(gdb)2112CallXactCallbacks(is_parallel_worker?XACT_EVENT_PARALLEL_COMMIT(gdb)
释放资源
(gdb)2115ResourceOwnerRelease(TopTransactionResourceOwner,(gdb)2120AtEOXact_Buffers(true);(gdb)2123AtEOXact_RelationCache(true);(gdb)2132AtEOXact_Inval(true);(gdb)2134AtEOXact_MultiXact();(gdb)2136ResourceOwnerRelease(TopTransactionResourceOwner,(gdb)2139ResourceOwnerRelease(TopTransactionResourceOwner,
执行清理操作
(gdb)2152smgrDoPendingDeletes(true);(gdb)2154AtCommit_Notify();(gdb)2155AtEOXact_GUC(true,1);(gdb)2156AtEOXact_SPI(true);(gdb)2157AtEOXact_on_commit_actions(true);(gdb)2158AtEOXact_Namespace(true,is_parallel_worker);(gdb)2159AtEOXact_SMgr();(gdb)2160AtEOXact_Files(true);(gdb)2161AtEOXact_ComboCid();(gdb)2162AtEOXact_HashTables(true);(gdb)2163AtEOXact_PgStat(true);(gdb)2164AtEOXact_Snapshot(true,false);(gdb)2165AtEOXact_ApplyLauncher(true);(gdb)2166pgstat_report_xact_timestamp(0);(gdb)2168CurrentResourceOwner=NULL;(gdb)2169ResourceOwnerDelete(TopTransactionResourceOwner);(gdb)2170s->curTransactionOwner=NULL;(gdb)
重置事务状态
(gdb)2171CurTransactionResourceOwner=NULL;(gdb)2172TopTransactionResourceOwner=NULL;(gdb)2174AtCommit_Memory();(gdb)2176s->transactionId=InvalidTransactionId;(gdb)2177s->subTransactionId=InvalidSubTransactionId;(gdb)2178s->nestingLevel=0;(gdb)2179s->gucNestLevel=0;(gdb)2180s->childXids=NULL;(gdb)2181s->nChildXids=0;(gdb)2182s->maxChildXids=0;(gdb)2184XactTopTransactionId=InvalidTransactionId;(gdb)2185nParallelCurrentXids=0;(gdb)2191s->state=TRANS_DEFAULT;(gdb)2193RESUME_INTERRUPTS();(gdb)2194}(gdb)
重置后的事务状态
(gdb)p*s$2={transactionId=0,subTransactionId=0,name=0x0,savepointLevel=0,state=TRANS_DEFAULT,blockState=TBLOCK_END,nestingLevel=0,gucNestLevel=0,curTransactionContext=0x0,curTransactionOwner=0x0,childXids=0x0,nChildXids=0,maxChildXids=0,prevUser=10,prevSecContext=0,prevXactReadOnly=false,startedInRecovery=false,didLogXid=true,parallelModeLevel=0,parent=0x0}(gdb)
执行完毕
(gdb)nCommitTransactionCommand()atxact.c:28322832s->blockState=TBLOCK_DEFAULT;(gdb)
到此,关于“PostgreSQL中函数CommitTransaction的实现逻辑是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。