怎么理解PostgreSQL事务管理中的子事务
本篇内容主要讲解“怎么理解PostgreSQL事务管理中的子事务”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解PostgreSQL事务管理中的子事务”吧!
一、Subtransaction HandlingREADME
SubtransactionHandling-----------------------子事务处理SubtransactionsareimplementedusingastackofTransactionStatestructures,eachofwhichhasapointertoitsparenttransaction'sstruct.Whenanewsubtransactionistobeopened,PushTransactioniscalled,whichcreatesanewTransactionState,withitsparentlinkpointingtothecurrenttransaction.StartSubTransactionisinchargeofinitializingthenewTransactionStatetosanevalues,andproperlyinitializingothersubsystems(AtSubStartroutines).子事务通过TransactionState结构体栈来实现,每一个TransactionState都有指向其父事务结构体的指针.新的子事务即将开启时,调用PushTransaction,该函数创建TransactionState结构体,parent指向当前事务.StartSubTransaction负责完整初始化新的TransactionState结构体,并正确初始化其他子系统(AtSubStart例程实现).Whenclosingasubtransaction,eitherCommitSubTransactionhastobecalled(ifthesubtransactioniscommitting),orAbortSubTransactionandCleanupSubTransaction(ifit'saborting).Ineithercase,PopTransactioniscalledsothesystemreturnstotheparenttransaction.在关闭子事务时,成功则调用CommitSubTransaction,取消/失败则调用AbortSubTransaction和CleanupSubTransaction.无论哪种情况,都会调用PopTransaction以便返回到父事务.Oneimportantpointregardingsubtransactionhandlingisthatseveralmayneedtobeclosedinresponsetoasingleusercommand.That'sbecausesavepointshavenames,andweallowtocommitorrollbackasavepointbyname,whichisnotnecessarilytheonethatwaslastopened.AlsoaCOMMITorROLLBACKcommandmustbeabletocloseouttheentirestack.Wehandlethisbyhavingtheutilitycommandsubroutinemarkallthestatestackentriesascommit-pendingorabort-pending,andthenwhenthemainloopreachesCommitTransactionCommand,therealworkisdone.Themainpointofdoingthingsthiswayisthatifwegetanerrorwhilepoppingstatestackentries,theremainingstackentriesstillshowwhatweneedtodotofinishup.子事务处理一个很很重要的点是为了响应单个用户命令需要关闭多个子事务.这是因为对于命名savepoints,PG允许通过名称提交或回滚至这些savepoint,而这些savepoint并不需要是最后才打开的那个.同时,COMMIT/ROLLBACK命令必须可以关闭整个栈.通过工具命令子例程来标记整个状态栈为commit-pending或者是abort-pending,然后在主循环到达CommitTransactionCommand时,执行实际的commit/abort.以这种方法做这些事情的主要关注点是如果在栈条目出栈时出现错误,剩余的栈条目仍然可以展示我们需要做什么才能完结.InthecaseofROLLBACKTO<savepoint>,weabortallthesubtransactionsupthroughtheoneidentifiedbythesavepointname,andthenre-createthatsubtransactionlevelwiththesamename.Soit'sacompletelynewsubtransactionasfarastheinternalsareconcerned.在ROLLBACKTO<savepoint>这种情况中,我们取消了从savepointname到当前的所有子事务,然后使用相同的名称重建了该子事务.因此这是内部需要关注的完全的子事务.Othersubsystemsareallowedtostart"internal"subtransactions,whicharehandledbyBeginInternalSubTransaction.Thisistoallowimplementingexceptionhandling,e.g.inPL/pgSQL.ReleaseCurrentSubTransactionandRollbackAndReleaseCurrentSubTransactionallowsthesubsystemtoclosesaidsubtransactions.Themaindifferencebetweenthisandthesavepoint/releasepathisthatweexecutethecompletestatetransitionimmediatelyineachsubroutine,ratherthandeferringsomeworkuntilCommitTransactionCommand.AnotherdifferenceisthatBeginInternalSubTransactionisallowedwhennoexplicittransactionblockhasbeenestablished,whileDefineSavepointisnot.其他子系统允许通过BeginInternalSubTransaction函数启动"internal"子事务.因此可以在诸如PL/pgSQL等编程语言中可以实现异常处理.ReleaseCurrentSubTransaction和RollbackAndReleaseCurrentSubTransaction允许子系统关闭子事务.这种方式跟savepoint/release的不同点是在每一个子例程中我们马上执行了完整的状态变换,而不是等到执行CommitTransactionCommand才执行某些工作.另外一个不同点是在没有创建显式事务块的情况下允许调用BeginInternalSubTransaction,而savepoint则需要明确的DefineSavepoint.TransactionandSubtransactionNumbering----------------------------------------事务和子事务编号TransactionsandsubtransactionsareassignedpermanentXIDsonlywhen/iftheyfirstdosomethingthatrequiresone---typically,insert/update/deleteatuple,thoughthereareafewotherplacesthatneedanXIDassigned.IfasubtransactionrequiresanXID,wealwaysfirstassignonetoitsparent.ThismaintainstheinvariantthatchildtransactionshaveXIDslaterthantheirparents,whichisassumedinanumberofplaces.事务和子事务在需要时才会分配持久的XIDs,典型的场景是insert/update/delete元组.如果子事务需要XID,首先会给其父事务分配一个,这保证了子事务编号在父事务之后.ThesubsidiaryactionsofobtainingalockontheXIDandenteringitintopg_subtransandPG_PROCaredoneatthetimeitisassigned.分配事务号还需要做的事情是在XID获取锁/写入到pg_subtrans和PG_PROC中.AtransactionthathasnoXIDstillneedstobeidentifiedforvariouspurposes,notablyholdinglocks.Forthispurposeweassigna"virtualtransactionID"orVXIDtoeachtop-leveltransaction.VXIDsareformedfromtwofields,thebackendIDandabackend-localcounter;thisarrangementallowsassignmentofanewVXIDattransactionstartwithoutanycontentionforsharedmemory.ToensurethataVXIDisn'tre-usedtoosoonafterbackendexit,westorethelastlocalcountervalueintosharedmemoryatbackendexit,andinitializeitfromthepreviousvalueforthesamebackendIDslotatbackendstart.Allthesecountersgobacktozeroatsharedmemoryre-initialization,butthat'sOKbecauseVXIDsneverappearanywhereon-disk.没有XID的事务仍然需要进行标识,特别是需要持有锁的时候.出于这个目的,我们分配了"虚拟事务号"(即VXID)给每一个顶层事务.VXIDs由两个域组成,后台进程ID和后台进程本地计数器;这样的编号产生方法不需要共享内存争用就可以进行新VXID的分配.为了确保在后台进程退出后VXID不会过快的被使用,我们把最后的本地计数器值存储到共享内存中,对于同一个后台进程ID,分配先前存储的计数器值给这个新的后台进程.在共享内存重新初始化后这些计数器会归零,由于不会出现落盘,因此这样的处理没有任何问题.Internally,abackendneedsawaytoidentifysubtransactionswhetherornottheyhaveXIDs;butthisneedonlylastsaslongastheparenttoptransactionendures.Therefore,wehaveSubTransactionId,whichissomewhatlikeCommandIdinthatit'sgeneratedfromacounterthatweresetatthestartofeachtoptransaction.Thetop-leveltransactionitselfhasSubTransactionId1,andsubtransactionshaveIDs2andup.(ZeroisreservedforInvalidSubTransactionId.)NotethatsubtransactionsdonothavetheirownVXIDs;theyusetheparenttoptransaction'sVXID.在内部实现上,不论子事务是否拥有XIDs,后台进程需要标识子事务的方法;只要父顶级事务存在这种需求就好一直存在.因此,产生了SubTransactionId,该字段类似于CommandId,在每次顶层事务都会重置的计数器.顶层事务本身的SubTransactionId设定为1,其他子事务的ID为2或更大(0保留用于InvalidSubTransactionId).注意子事务没有VXIDs;它们使用顶层事务的VXID.
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.*/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*/TBLOCK_END,/*COMMITreceived*/TBLOCK_ABORT,/*failedxact,awaitingROLLBACK*/TBLOCK_ABORT_END,/*failedxact,ROLLBACKreceived*/TBLOCK_ABORT_PENDING,/*livexact,ROLLBACKreceived*/TBLOCK_PREPARE,/*livexact,PREPAREreceived*//*subtransactionstates*/TBLOCK_SUBBEGIN,/*startingasubtransaction*/TBLOCK_SUBINPROGRESS,/*livesubtransaction*/TBLOCK_SUBRELEASE,/*RELEASEreceived*/TBLOCK_SUBCOMMIT,/*COMMITreceivedwhileTBLOCK_SUBINPROGRESS*/TBLOCK_SUBABORT,/*failedsubxact,awaitingROLLBACK*/TBLOCK_SUBABORT_END,/*failedsubxact,ROLLBACKreceived*/TBLOCK_SUBABORT_PENDING,/*livesubxact,ROLLBACKreceived*/TBLOCK_SUBRESTART,/*livesubxact,ROLLBACKTOreceived*/TBLOCK_SUBABORT_RESTART/*failedsubxact,ROLLBACKTOreceived*/}TBlockState;/**transactionstatestructure*/typedefstructTransactionStateData{FullTransactionIdfullTransactionId;/*myFullTransactionId*/SubTransactionIdsubTransactionId;/*mysubxactID*/char*name;/*savepointname,ifany*/intsavepointLevel;/*savepointlevel*/TransStatestate;/*low-levelstate*/TBlockStateblockState;/*high-levelstate*/intnestingLevel;/*transactionnestingdepth*/intgucNestLevel;/*GUCcontextnestingdepth*/MemoryContextcurTransactionContext;/*myxact-lifetimecontext*/ResourceOwnercurTransactionOwner;/*myqueryresources*/TransactionId*childXids;/*subcommittedchildXIDs,inXIDorder*/intnChildXids;/*#ofsubcommittedchildXIDs*/intmaxChildXids;/*allocatedsizeofchildXids[]*/OidprevUser;/*previousCurrentUserIdsetting*/intprevSecContext;/*previousSecurityRestrictionContext*/boolprevXactReadOnly;/*entry-timexactr/ostate*/boolstartedInRecovery;/*didwestartinrecovery?*/booldidLogXid;/*hasxidbeenincludedinWALrecord?*/intparallelModeLevel;/*Enter/ExitParallelModecounter*/boolchain;/*startanewblockafterthisone*/structTransactionStateData*parent;/*backlinktoparent*/}TransactionStateData;typedefTransactionStateData*TransactionState;
到此,相信大家对“怎么理解PostgreSQL事务管理中的子事务”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。