这篇文章主要讲解了“PostgreSQL中函数StartTransaction的实现逻辑是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中函数StartTransaction的实现逻辑是什么”吧!

一、数据结构

静态变量
当前事务状态CurrentTransactionState

/**CurrentTransactionStatealwayspointstothecurrenttransactionstate*block.ItwillpointtoTopTransactionStateDatawhennotina*transactionatall,orwheninatop-leveltransaction.*CurrentTransactionState通常指向当前事务块.*如不处于事务中或者处于顶层事务中,则指向TopTransactionStateData*/staticTransactionStateDataTopTransactionStateData={.state=TRANS_DEFAULT,.blockState=TBLOCK_DEFAULT,};/**unreportedXidsholdsXIDsofallsubtransactionsthathavenotyetbeen*reportedinanXLOG_XACT_ASSIGNMENTrecord.*unreportedXids保存所有尚未在XLOG_XACT_ASSIGNMENT记录的子事务.*/staticintnUnreportedXids;staticTransactionIdunreportedXids[PGPROC_MAX_CACHED_SUBXIDS];staticTransactionStateCurrentTransactionState=&TopTransactionStateData;/**ThesubtransactionIDandcommandIDassignmentcountersareglobal*toawholetransaction,sowedonotkeeptheminthestatestack.*subtransactionID和commandID全局计数器,对事务可见,在state栈中不记录这些信息.*/staticSubTransactionIdcurrentSubTransactionId;staticCommandIdcurrentCommandId;staticboolcurrentCommandIdUsed;

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;

VirtualTransactionId
VirtualTransactionIDs由执行事务的后台进程BackendId和逻辑分配的LocalTransactionId组成.

/**Top-leveltransactionsareidentifiedbyVirtualTransactionIDscomprising*theBackendIdofthebackendrunningthexact,plusalocally-assigned*LocalTransactionId.Theseareguaranteeduniqueovertheshortterm,*butwillbereusedafteradatabaserestart;hencetheyshouldnever*bestoredondisk.*最高层的事务通过VirtualTransactionIDs定义.*VirtualTransactionIDs由执行事务的后台进程BackendId和逻辑分配的LocalTransactionId组成.**NotethatstructVirtualTransactionIdcannotbeassumedtobeatomically*assignableasawhole.However,typeLocalTransactionIdisassumedto*beatomicallyassignable,andthebackendIDdoesn'tchangeoftenenough*tobeaproblem,sowecanfetchorassignthetwofieldsseparately.*WedeliberatelyrefrainfromusingthestructwithinPGPROC,toprevent*codingerrorsfromtryingtousestructassignmentwithit;insteaduse*GET_VXID_FROM_PGPROC().*请注意,不能假设structVirtualTransactionId作为一个整体是原子可分配的。*但是,类型LocalTransactionId是假定原子可分配的,同时后台进程ID不会经常变换,因此这不是一个问题,*因此我们可以单独提取或者分配这两个域字段.**/typedefstruct{BackendIdbackendId;/*determinedatbackendstartup*/LocalTransactionIdlocalTransactionId;/*backend-localtransactionid*/}VirtualTransactionId;二、源码解读

StartTransaction函数,用于启动事务,设置事务状态为TRANS_INPROGRESS,CurrentTransactionState->state = TRANS_INPROGRESS.

/**StartTransaction*启动事务*/staticvoidStartTransaction(void){TransactionStates;//事务状态VirtualTransactionIdvxid;//虚拟事务ID/**Let'sjustmakesurethestatestackisempty*确保事务栈是空的*/s=&TopTransactionStateData;CurrentTransactionState=s;Assert(XactTopTransactionId==InvalidTransactionId);/*checkthecurrenttransactionstate*///检查当前事务状态Assert(s->state==TRANS_DEFAULT);/**Setthecurrenttransactionstateinformationappropriatelyduring*startprocessing.Notethatoncethetransactionstatusisswitched*thisprocesscannotfailuntiltheuserIDandthesecuritycontext*flagsarefetchedbelow.*在启动过程中设置当前事务状态信息。*请注意,一旦切换了事务状态,在后续获取用户ID和安全上下文标志前,不会出现异常。*/s->state=TRANS_START;//无效事务ID,待分配s->transactionId=InvalidTransactionId;/*untilassigned*//**initializecurrenttransactionstatefields*初始化当前事务状态字段**note:prevXactReadOnlyisnotusedattheoutermostlevel*注意:prevXactReadOnly不会在最外层中使用*/s->nestingLevel=1;s->gucNestLevel=1;s->childXids=NULL;s->nChildXids=0;s->maxChildXids=0;/**OncethecurrentuserIDandthesecuritycontextflagsarefetched,*bothwillbeproperlyreseteveniftransactionstartupfails.*一旦当前用户ID和安全上下文标记已提取,即使事务启动失败,也会正确地重置它们。*/GetUserIdAndSecContext(&s->prevUser,&s->prevSecContext);/*SecurityRestrictionContextshouldneverbesetoutsideatransaction*///SecurityRestrictionContext不应在事务外设置Assert(s->prevSecContext==0);/**Makesurewe'veresetxactstatevariables*确保已重置了xact状态变量**Ifrecoveryisstillinprogress,markthistransactionasread-only.*WehavelowerleveldefencesinXLogInsertandelsewheretostopus*frommodifyingdataduringrecovery,butthisgivesthenormal*indicationtotheuserthatthetransactionisread-only.*如仍处于恢复过程,标志此事务为只读.*在XLogInsert中和其他地方有低级别的保护机制确保在恢复过程中不会更新数据,*只是给用户正常的提示,说明事务只读.*/if(RecoveryInProgress()){//只读状态s->startedInRecovery=true;XactReadOnly=true;}else{s->startedInRecovery=false;XactReadOnly=DefaultXactReadOnly;}XactDeferrable=DefaultXactDeferrable;XactIsoLevel=DefaultXactIsoLevel;forceSyncCommit=false;MyXactFlags=0;/**reinitializewithin-transactioncounters*重新初始化事务内计数器*/s->subTransactionId=TopSubTransactionId;currentSubTransactionId=TopSubTransactionId;currentCommandId=FirstCommandId;currentCommandIdUsed=false;/**initializereportedxidaccounting*初始化已报告的事务计数*/nUnreportedXids=0;s->didLogXid=false;/**mustinitializeresource-managementstufffirst*必须首先初始化资源管理器*/AtStart_Memory();AtStart_ResourceOwner();/**AssignanewLocalTransactionId,andcombineitwiththebackendIdto*formavirtualtransactionid.*分配新的本地事务ID(LocalTransactionId),*与backendId组成虚拟事务ID.*/vxid.backendId=MyBackendId;vxid.localTransactionId=GetNextLocalTransactionId();/**Lockthevirtualtransactionidbeforeweannounceitintheprocarray*在procarray声明前,锁定虚拟事务ID*/VirtualXactLockTableInsert(vxid);/**Advertiseitintheprocarray.Weassumeassignmentof*LocalTransactionIDisatomic,andthebackendIdshouldbesetalready.*在procarray中声明.*假定LocalTransactionID是原子的,backendId已分配.*/Assert(MyProc->backendId==vxid.backendId);MyProc->lxid=vxid.localTransactionId;TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);/**settransaction_timestamp()(a/k/anow()).Normally,wewantthisto*bethesameasthefirstcommand'sstatement_timestamp(),sodon'tdoa*freshGetCurrentTimestamp()call(which'dbeexpensiveanyway).But*fortransactionsstartedinsideprocedures(i.e.,nonatomicSPI*contexts),wedoneedtoadvancethetimestamp.Also,inaparallel*worker,thetimestampshouldalreadyhavebeenprovidedbyacallto*SetParallelStartTimestamps().*设置transaction_timestamp.*正常来说,期望该值与第一条命令的statement_timestamp一样,这样就不需要*调用GetCurrentTimestamp进行刷新(昂贵的操作!).*但对于在过程中启动的事务(如非原子的SPI上下文),我们确实需要增加时间戳.*同样的,在并行worker中,时间戳应通过外层调用SetParallelStartTimestamps提供.*/if(!IsParallelWorker()){if(!SPI_inside_nonatomic_context())xactStartTimestamp=stmtStartTimestamp;elsexactStartTimestamp=GetCurrentTimestamp();}elseAssert(xactStartTimestamp!=0);pgstat_report_xact_timestamp(xactStartTimestamp);/*MarkxactStopTimestampasunset.*///标记xactStopTimestamp未设置xactStopTimestamp=0;/**initializeothersubsystemsfornewtransaction*为新事务初始化其他子系统(GUC/Cache等)*/AtStart_GUC();AtStart_Cache();AfterTriggerBeginXact();/**donewithstartprocessing,setcurrenttransactionstateto"in*progress"*已完成启动过程,设置事务状态为TRANS_INPROGRESS*/s->state=TRANS_INPROGRESS;ShowTransactionState("StartTransaction");}三、跟踪分析

执行begin,触发该函数调用

11:10:36(xdb@[local]:5432)testdb=#begin;

启动gdb,设置断点

(gdb)bStartTransactionBreakpoint4at0x54800f:filexact.c,line1825.(gdb)cContinuing.Breakpoint4,StartTransaction()atxact.c:18251825s=&TopTransactionStateData;(gdb)

查看调用栈

(gdb)bt#0StartTransaction()atxact.c:1825#10x0000000000548f50inStartTransactionCommand()atxact.c:2718#20x00000000008c8e7dinstart_xact_command()atpostgres.c:2500#30x00000000008c6771inexec_simple_query(query_string=0x24a6ec8"begin;")atpostgres.c:948#40x00000000008cae70inPostgresMain(argc=1,argv=0x24d2dc8,dbname=0x24d2c30"testdb",username=0x24a3ba8"xdb")atpostgres.c:4182#50x000000000082642binBackendRun(port=0x24c8c00)atpostmaster.c:4361#60x0000000000825b8finBackendStartup(port=0x24c8c00)atpostmaster.c:4033#70x0000000000821f1cinServerLoop()atpostmaster.c:1706#80x00000000008217b4inPostmasterMain(argc=1,argv=0x24a1b60)atpostmaster.c:1379#90x00000000007488efinmain(argc=1,argv=0x24a1b60)atmain.c:228(gdb)

查看TopTransactionStateData全局变量(尚未初始化)

(gdb)pTopTransactionStateData$7={transactionId=0,subTransactionId=0,name=0x0,savepointLevel=0,state=TRANS_DEFAULT,blockState=TBLOCK_DEFAULT,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}

设置全局变量CurrentTransactionState = & TopTransactionStateData;

(gdb)n1826CurrentTransactionState=s;(gdb)1828Assert(XactTopTransactionId==InvalidTransactionId);(gdb)

初始化事务状态

(gdb)n1833if(s->state!=TRANS_DEFAULT)(gdb)1841s->state=TRANS_START;(gdb)1842s->transactionId=InvalidTransactionId;/*untilassigned*/(gdb)1852if(RecoveryInProgress())(gdb)1859s->startedInRecovery=false;(gdb)1860XactReadOnly=DefaultXactReadOnly;(gdb)1862XactDeferrable=DefaultXactDeferrable;(gdb)1863XactIsoLevel=DefaultXactIsoLevel;(gdb)1864forceSyncCommit=false;(gdb)1865MyXactFlags=0;(gdb)1870s->subTransactionId=TopSubTransactionId;(gdb)1871currentSubTransactionId=TopSubTransactionId;(gdb)1872currentCommandId=FirstCommandId;(gdb)1873currentCommandIdUsed=false;(gdb)1878nUnreportedXids=0;(gdb)1879s->didLogXid=false;(gdb)1884AtStart_Memory();(gdb)

启动subsystem(内存/GUC/Cache等)

(gdb)1884AtStart_Memory();(gdb)n1885AtStart_ResourceOwner();(gdb)

设置虚拟事务ID

1891vxid.backendId=MyBackendId;(gdb)1892vxid.localTransactionId=GetNextLocalTransactionId();(gdb)1897VirtualXactLockTableInsert(vxid);(gdb)1903Assert(MyProc->backendId==vxid.backendId);(gdb)pvxid$8={backendId=3,localTransactionId=6}(gdb)(gdb)n1904MyProc->lxid=vxid.localTransactionId;(gdb)

设置时间戳

1906TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);(gdb)1917if(!IsParallelWorker())(gdb)1919if(!SPI_inside_nonatomic_context())(gdb)1920xactStartTimestamp=stmtStartTimestamp;(gdb)1926pgstat_report_xact_timestamp(xactStartTimestamp);(gdb)1928xactStopTimestamp=0;(gdb)(gdb)pxactStartTimestamp$9=601009839154257

初始化其他字段

(gdb)n1935s->nestingLevel=1;(gdb)n1936s->gucNestLevel=1;(gdb)1937s->childXids=NULL;(gdb)1938s->nChildXids=0;(gdb)1939s->maxChildXids=0;(gdb)1940GetUserIdAndSecContext(&s->prevUser,&s->prevSecContext);(gdb)1942Assert(s->prevSecContext==0);(gdb)1947AtStart_GUC();(gdb)1948AtStart_Cache();(gdb)1949AfterTriggerBeginXact();(gdb)1955s->state=TRANS_INPROGRESS;(gdb)1957ShowTransactionState("StartTransaction");(gdb)1958}(gdb)

初始化后的事务状态

(gdb)p*s$10={transactionId=0,subTransactionId=1,name=0x0,savepointLevel=0,state=TRANS_INPROGRESS,blockState=TBLOCK_DEFAULT,nestingLevel=1,gucNestLevel=1,curTransactionContext=0x2523850,curTransactionOwner=0x24d4868,childXids=0x0,nChildXids=0,maxChildXids=0,prevUser=10,prevSecContext=0,prevXactReadOnly=false,startedInRecovery=false,didLogXid=false,parallelModeLevel=0,parent=0x0}(gdb)

完成调用

(gdb)nStartTransactionCommand()atxact.c:27192719s->blockState=TBLOCK_STARTED;(gdb)2720break;(gdb)

感谢各位的阅读,以上就是“PostgreSQL中函数StartTransaction的实现逻辑是什么”的内容了,经过本文的学习后,相信大家对PostgreSQL中函数StartTransaction的实现逻辑是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!