PostgreSQL中插入数据时与WAL相关的处理逻辑是什么
本篇内容主要讲解“PostgreSQL中插入数据时与WAL相关的处理逻辑是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL中插入数据时与WAL相关的处理逻辑是什么”吧!
一、数据结构静态变量
进程中全局共享
staticintnum_rdatas;/*entriescurrentlyused*///已分配的空间大小staticintmax_rdatas;/*allocatedsize*///是否调用XLogBeginInsert函数staticboolbegininsert_called=false;staticXLogCtlData*XLogCtl=NULL;/*flagsforthein-progressinsertion*/staticuint8curinsert_flags=0;/**ProcLastRecPtrpointstothestartofthelastXLOGrecordinsertedbythe*currentbackend.Itisupdatedforallinserts.XactLastRecEndpointsto*end+1ofthelastrecord,andisresetwhenweendatop-leveltransaction,*orstartanewone;soitcanbeusedtotellifthecurrenttransactionhas*createdanyXLOGrecords.*ProcLastRecPtr指向当前后端插入的最后一条XLOG记录的开头。*它针对所有插入进行更新。*XactLastRecEnd指向最后一条记录的末尾位置+1,*并在结束顶级事务或启动新事务时重置;*因此,它可以用来判断当前事务是否创建了任何XLOG记录。**Whileinparallelmode,thismaynotbefullyuptodate.Whencommitting,*atransactioncanassumethiscoversallxlogrecordswritteneitherbythe*userbackendorbyanyparallelworkerwhichwaspresentatanypointduring*thetransaction.Butwhenaborting,orwhenstillinparallelmode,other*parallelbackendsmayhavewrittenWALrecordsatlaterLSNsthanthevalue*storedhere.Theparallelleaderadvancesitsowncopy,whennecessary,*inWaitForParallelWorkersToFinish.*在并行模式下,这可能不是完全是最新的。*在提交时,事务可以假定覆盖了用户后台进程或在事务期间出现的并行worker进程的所有xlog记录。*但是,当中止时,或者仍然处于并行模式时,其他并行后台进程可能在较晚的LSNs中写入了WAL记录,*而不是存储在这里的值。*当需要时,并行处理进程的leader在WaitForParallelWorkersToFinish中会推进自己的副本。*/XLogRecPtrProcLastRecPtr=InvalidXLogRecPtr;XLogRecPtrXactLastRecEnd=InvalidXLogRecPtr;XLogRecPtrXactLastCommitEnd=InvalidXLogRecPtr;/*ForWALInsertLockAcquire/Releasefunctions*///用于WALInsertLockAcquire/Release函数staticintMyLockNo=0;staticboolholdingAllLocks=false;
宏定义
typedefchar*Pointer;//指针typedefPointerPage;//Page#defineXLOG_HEAP_INSERT0x00/**PointertoalocationintheXLOG.Thesepointersare64bitswide,*becausewedon'twantthemevertooverflow.*指向XLOG中的位置.*这些指针大小为64bit,以确保指针不会溢出.*/typedefuint64XLogRecPtr;/**Additionalmacrosforaccesstopageheaders.(Bewaremultipleevaluation*ofthearguments!)*/#definePageGetLSN(page)\PageXLogRecPtrGet(((PageHeader)(page))->pd_lsn)#definePageSetLSN(page,lsn)\PageXLogRecPtrSet(((PageHeader)(page))->pd_lsn,lsn)/*Buffersizerequiredtostoreacompressedversionofbackupblockimage*///存储压缩会后的块镜像所需要的缓存空间大小#definePGLZ_MAX_BLCKSZPGLZ_MAX_OUTPUT(BLCKSZ)//--------------------------------------------------锁相关/**Fakespinlockimplementationusingsemaphores---slowandprone*tofallfoulofkernellimitsonnumberofsemaphores,sodon'tusethis*unlessyoumust!Thesubroutinesappearinspin.c.*使用信号量的伪自旋锁实现——很慢而且容易与内核对信号量的限制相冲突,*所以除非必须,否则不要使用它!子例程出现在spin.c中。*/typedefintslock_t;typedefuint32pg_crc32c;#defineSpinLockInit(lock)S_INIT_LOCK(lock)#defineSpinLockAcquire(lock)S_LOCK(lock)#defineSpinLockRelease(lock)S_UNLOCK(lock)#defineSpinLockFree(lock)S_LOCK_FREE(lock)#defineXLogSegmentOffset(xlogptr,wal_segsz_bytes)\((xlogptr)&((wal_segsz_bytes)-1))#defineLW_FLAG_HAS_WAITERS((uint32)1<<30)#defineLW_FLAG_RELEASE_OK((uint32)1<<29)#defineLW_FLAG_LOCKED((uint32)1<<28)#defineLW_VAL_EXCLUSIVE((uint32)1<<24)#defineLW_VAL_SHARED1#defineLW_LOCK_MASK((uint32)((1<<25)-1))/*MustbegreaterthanMAX_BACKENDS-whichis2^23-1,sowe'refine.*/#defineLW_SHARED_MASK((uint32)((1<<24)-1))
LWLock
lwlock.c外的代码不应直接操作这个结构的内容,但我们必须声明该结构体以便将LWLocks合并到其他数据结构中。
/**Codeoutsideoflwlock.cshouldnotmanipulatethecontentsofthis*structuredirectly,butwehavetodeclareitheretoallowLWLockstobe*incorporatedintootherdatastructures.*lwlock.c外的代码不应直接操作这个结构的内容,*但我们必须声明该结构体以便将LWLocks合并到其他数据结构中。*/typedefstructLWLock{uint16tranche;/*trancheID*///独占/非独占locker的状态pg_atomic_uint32state;/*stateofexclusive/nonexclusivelockers*///正在等待的PGPROCs链表proclist_headwaiters;/*listofwaitingPGPROCs*/#ifdefLOCK_DEBUG//用于DEBUG//waiters的数量pg_atomic_uint32nwaiters;/*numberofwaiters*///锁的最后独占者structPGPROC*owner;/*lastexclusiveownerofthelock*/#endif}LWLock;二、源码解读
heap_insert
主要实现逻辑是插入元组到堆中,其中存在对WAL(XLog)进行处理的部分.
参见PostgreSQL 源码解读(104)- WAL#1(Insert & WAL-heap_insert函数#1)
XLogInsert/XLogInsertRecord
插入一个具有指定的RMID和info字节的XLOG记录,该记录的主体是先前通过XLogRegister*调用注册的数据和缓冲区引用。
参见PostgreSQL 源码解读(106)- WAL#3(Insert & WAL-heap_insert函数#3)
WALInsertLockXXX
包括WALInsertLockAcquireExclusive、WALInsertLockAcquire和WALInsertLockRelease等
//-----------------------------------------------------------WALInsertLockAcquireExclusive/**AcquireallWALinsertionlocks,topreventotherbackendsfrominserting*toWAL.*请求所有的WALinsertion锁,以避免其他后台进程插入数据到WAL中*/staticvoidWALInsertLockAcquireExclusive(void){inti;/**Whenholdingallthelocks,allbutthelastlock'sinsertingAt*indicatorissetto0xFFFFFFFFFFFFFFFF,whichishigherthananyreal*XLogRecPtrvalue,tomakesurethatno-oneblockswaitingonthose.*在持有所有的locks时,除了最后一个锁的insertingAt指示器外,*其余均设置为0xFFFFFFFFFFFFFFFF,*该值比所有实际的XLogRecPtr都要大,以确保没有阻塞这些锁。.*/for(i=0;i<NUM_XLOGINSERT_LOCKS-1;i++)//NUM_XLOGINSERT_LOCKS{LWLockAcquire(&WALInsertLocks[i].l.lock,LW_EXCLUSIVE);LWLockUpdateVar(&WALInsertLocks[i].l.lock,&WALInsertLocks[i].l.insertingAt,PG_UINT64_MAX);}/*Variablevalueresetto0atrelease*///在释放时,变量值重置为0LWLockAcquire(&WALInsertLocks[i].l.lock,LW_EXCLUSIVE);//设置标记holdingAllLocks=true;}/**LWLockAcquire-acquirealightweightlockinthespecifiedmode*LWLockAcquire-申请指定模式的轻量级锁**Ifthelockisnotavailable,sleepuntilitis.Returnstrueifthelock*wasavailableimmediately,falseifwehadtosleep.*如果锁不可用,休眠直至可用.*如果锁马上可用则返回T,需要休眠则返回F**Sideeffect:cancel/dieinterruptsareheldoffuntillockrelease.*副作用:在锁释放的时候才能允许中断/终止*/boolLWLockAcquire(LWLock*lock,LWLockModemode){PGPROC*proc=MyProc;//PGPROC数据结构boolresult=true;intextraWaits=0;#ifdefLWLOCK_STATSlwlock_stats*lwstats;lwstats=get_lwlock_stats_entry(lock);//获得锁的统计入口#endif//模式验证AssertArg(mode==LW_SHARED||mode==LW_EXCLUSIVE);PRINT_LWDEBUG("LWLockAcquire",lock,mode);#ifdefLWLOCK_STATS/*Countlockacquisitionattempts*/if(mode==LW_EXCLUSIVE)lwstats->ex_acquire_count++;elselwstats->sh_acquire_count++;#endif/*LWLOCK_STATS*//**Wecan'twaitifwehaven'tgotaPGPROC.Thisshouldonlyoccur*duringbootstraporsharedmemoryinitialization.PutanAsserthere*tocatchunsafecodingpractices.*如果还没有得到PGPROC则不能等待.*这种情况可能出现在bootstrap或者共享内存初始化时.*在这里加入Assert代码以确保捕获不安全的编码实践.*/Assert(!(proc==NULL&&IsUnderPostmaster));/*Ensurewewillhaveroomtorememberthelock*///确保我们有足够的地方存储锁if(num_held_lwlocks>=MAX_SIMUL_LWLOCKS)elog(ERROR,"toomanyLWLockstaken");/**Lockoutcancel/dieinterruptsuntilweexitthecodesectionprotected*bytheLWLock.Thisensuresthatinterruptswillnotinterferewith*manipulationsofdatastructuresinsharedmemory.*退出使用LWLock锁保护的实现逻辑时才能允许取消或者中断.*这样可以确保中断不会与共享内存中的数据结构管理逻辑发现关系.*/HOLD_INTERRUPTS();/**Loopheretotrytoacquirelockaftereachtimewearesignaledby*LWLockRelease.*循环,在每次LWLockRelease信号产生时获取锁.**NOTE:itmightseembettertohaveLWLockReleaseactuallygrantusthe*lock,ratherthanretryingandpossiblyhavingtogobacktosleep.But*inpracticethatisnogoodbecauseitmeansaprocessswapforevery*lockacquisitionwhentwoormoreprocessesarecontendingforthesame*lock.SinceLWLocksarenormallyusedtoprotectnot-very-long*sectionsofcomputation,aprocessneedstobeabletoacquireand*releasethesamelockmanytimesduringasingleCPUtimeslice,even*inthepresenceofcontention.Theefficiencyofbeingabletodothat*outweighstheinefficiencyofsometimeswastingaprocessdispatch*cyclebecausethelockisnotfreewhenareleasedwaiterfinallygets*torun.Seepgsql-hackersarchivesfor29-Dec-01.*注意:看起来相对于不断的重入和休眠而言LWLockRelease的实际持有者授予我们锁会更好,*但在工程实践上来看,*这样的做法并不好因为这意味着当两个或多个进程争用同一个锁时对每个锁都会出现进程交换.*由于LWLocks通常来说用于保护并不是太长时间的计算逻辑,*甚至在出现争用的时候,一个进程需要能够在一个CPU时间片期间获取和释放同样的锁很多次.*那样子做的收获会导致有时候进程调度的低效,*因为当一个已释放的进程终于可以运行时,锁却没有获取.*/for(;;){boolmustwait;/**Trytograbthelockthefirsttime,we'renotinthewaitqueue*yet/anymore.*第一次试着获取锁,我们已经不在等待队列中了。*/mustwait=LWLockAttemptLock(lock,mode);if(!mustwait){LOG_LWDEBUG("LWLockAcquire",lock,"immediatelyacquiredlock");break;/*成功!gotthelock*/}/**Ok,atthispointwecouldn'tgrabthelockonthefirsttry.We*cannotsimplyqueueourselvestotheendofthelistandwaittobe*wokenupbecausebynowthelockcouldlonghavebeenreleased.*Insteadaddustothequeueandtrytograbthelockagain.Ifwe*succeedweneedtorevertthequeuingandbehappy,otherwisewe*recheckthelock.Ifwestillcouldn'tgrabit,weknowthatthe*otherlockerwillseeourqueueentrieswhenreleasingsincethey*existedbeforewecheckedforthelock.*在这个点,我们不能在第一次就获取锁.*我们不能在链表的末尾进行简单的排队然后等待唤醒,因为锁可能已经释放很长时间了.*相反,我们需要重新加入到队列中再次尝试获取锁.*如果成功了,我们需要翻转队列,否则的话需要重新检查锁.*如果还是不能获取锁,我们知道其他locker在释放时可以看到我们的队列入口,*因为在我们检查锁时它们已经存在了.*//*addtothequeue*///添加到队列中LWLockQueueSelf(lock,mode);/*we'renowguaranteedtobewokenupifnecessary*///在需要的时候,确保可以被唤醒mustwait=LWLockAttemptLock(lock,mode);/*ok,grabbedthelockthesecondtimeround,needtoundoqueueing*///第二次尝试获取锁,需要取消排队if(!mustwait){LOG_LWDEBUG("LWLockAcquire",lock,"acquired,undoingqueue");LWLockDequeueSelf(lock);//出列break;}/**Waituntilawakened.*等待直至被唤醒**Sincewesharetheprocesswaitsemaphorewiththeregularlock*managerandProcWaitForSignal,andwemayneedtoacquireanLWLock*whileoneofthoseispending,itispossiblethatwegetawakened*forareasonotherthanbeingsignaledbyLWLockRelease.Ifso,*loopbackandwaitagain.Oncewe'vegottentheLWLock,*re-incrementthesemabythenumberofadditionalsignalsreceived,*sothatthelockmanagerorsignalmanagerwillseethereceived*signalwhenitnextwaits.*由于我们使用常规的锁管理和ProcWaitForSignal信号共享进程等待信号量,*我们可能需要在其中一个挂起时获取LWLock,*原因是有可能是由于其他的原因而不是通过LWLockRelease信号被唤醒.*如果是这种情况,则继续循环等待.*一旦我们获得LWLock,根据接收到的额外信号数目,再次增加信号量,*以便锁管理器或者信号管理器在下次等待时可以看到已接收的信号.*/LOG_LWDEBUG("LWLockAcquire",lock,"waiting");#ifdefLWLOCK_STATSlwstats->block_count++;//统计#endifLWLockReportWaitStart(lock);//报告等待TRACE_POSTGRESQL_LWLOCK_WAIT_START(T_NAME(lock),mode);for(;;){PGSemaphoreLock(proc->sem);if(!proc->lwWaiting)//如果不是LWLock等待,跳出循环break;extraWaits++;//额外的等待}/*Retrying,allowLWLockReleasetoreleasewaitersagain.*///重试,允许LWLockRelease再次释放waiterspg_atomic_fetch_or_u32(&lock->state,LW_FLAG_RELEASE_OK);#ifdefLOCK_DEBUG{/*notwaitinganymore*///无需等待uint32nwaitersPG_USED_FOR_ASSERTS_ONLY=pg_atomic_fetch_sub_u32(&lock->nwaiters,1);Assert(nwaiters<MAX_BACKENDS);}#endifTRACE_POSTGRESQL_LWLOCK_WAIT_DONE(T_NAME(lock),mode);LWLockReportWaitEnd();LOG_LWDEBUG("LWLockAcquire",lock,"awakened");//再次循环以再次请求锁/*Nowloopbackandtrytoacquirelockagain.*/result=false;}TRACE_POSTGRESQL_LWLOCK_ACQUIRE(T_NAME(lock),mode);//获取成功!/*Addlocktolistoflocksheldbythisbackend*///在该后台进程持有的锁链表中添加锁held_lwlocks[num_held_lwlocks].lock=lock;held_lwlocks[num_held_lwlocks++].mode=mode;/**Fixtheprocesswaitsemaphore'scountforanyabsorbedwakeups.*修正进程咋等待信号量计数的其他absorbed唤醒。*/while(extraWaits-->0)PGSemaphoreUnlock(proc->sem);returnresult;}/**Internalfunctionthattriestoatomicallyacquirethelwlockinthepassed*inmode.*尝试使用指定模式原子性获取LWLock锁的内部函数.**Thisfunctionwillnotblockwaitingforalocktobecomefree-that'sthe*callersjob.*该函数不会阻塞等待锁释放的进程--这是调用者的工作.**Returnstrueifthelockisn'tfreeandweneedtowait.*如果锁仍未释放,仍需要等待,则返回T*/staticboolLWLockAttemptLock(LWLock*lock,LWLockModemode){uint32old_state;AssertArg(mode==LW_EXCLUSIVE||mode==LW_SHARED);/**Readonceoutsidetheloop,lateriterationswillgetthenewervalue*viacompare&exchange.*在循环外先读取一次,后续可以通过比较和交换获得较新的值*/old_state=pg_atomic_read_u32(&lock->state);/*loopuntilwe'vedeterminedwhetherwecouldacquirethelockornot*///循环指针我们确定是否可以获得锁位置while(true){uint32desired_state;boollock_free;desired_state=old_state;if(mode==LW_EXCLUSIVE)//独占{lock_free=(old_state&LW_LOCK_MASK)==0;if(lock_free)desired_state+=LW_VAL_EXCLUSIVE;}else{//非独占lock_free=(old_state&LW_VAL_EXCLUSIVE)==0;if(lock_free)desired_state+=LW_VAL_SHARED;}/**Attempttoswapinthestateweareexpecting.Ifwedidn'tsee*locktobefree,that'sjusttheoldvalue.Ifwesawitasfree,*we'llattempttomarkitacquired.Thereasonthatwealwaysswap*inthevalueisthatthisdoublesasamemorybarrier.Wecouldtry*tobesmarterandonlyswapinvaluesifwesawthelockasfree,*butbenchmarkhaven'tshownitasbeneficialsofar.*尝试在我们期望的状态下进行交换。*如果没有看到锁被释放,那么这回是旧的值.*如果锁已释放,尝试标记锁已被获取.*我们通常交换值的理由是会使用双倍的内存barrier.*我们尝试变得更好:只交换我们看到已释放的锁,但压力测试显示并没有什么性能改善.**Retryifthevaluechangedsincewelastlookedatit.*在最后一次查找后如果值改变,则重试*/if(pg_atomic_compare_exchange_u32(&lock->state,&old_state,desired_state)){if(lock_free){/*Great!Gotthelock.*///很好,获取锁!#ifdefLOCK_DEBUGif(mode==LW_EXCLUSIVE)lock->owner=MyProc;#endifreturnfalse;}elsereturntrue;/*某人还持有锁.somebodyelsehasthelock*/}}pg_unreachable();//正常来说,程序逻辑不应到这里}//-----------------------------------------------------------WALInsertLockAcquire/**AcquireaWALinsertionlock,forinsertingtoWAL.*在写入WAL前获取wALinsertion锁*/staticvoidWALInsertLockAcquire(void){boolimmed;/**Itdoesn'tmatterwhichoftheWALinsertionlocksweacquire,sotry*theoneweusedlasttime.Ifthesystemisn'tparticularlybusy,it's*agoodbetthatit'sstillavailable,andit'sgoodtohavesome*affinitytoaparticularlocksothatyoudon'tunnecessarilybounce*cachelinesbetweenprocesseswhenthere'snocontention.*我们请求获取哪个WALinsertion锁无关紧要,因此获取最后使用的那个.*如果系统并不繁忙,那么运气好的话,仍然可用,*与特定的锁保持一定的亲缘关系是很好的,这样在没有争用的情况下,*就可用避免不必要地在进程之间切换缓存line。**Ifthisisthefirsttimethroughinthisbackend,pickalock*(semi-)randomly.Thisallowsthelockstobeusedevenlyifyouhavea*lotofveryshortconnections.*如果这是该进程的第一次获取,随机获取一个锁.*如果有很多非常短的连接的情况下,这样可以均匀地使用锁。*/staticintlockToTry=-1;if(lockToTry==-1)lockToTry=MyProc->pgprocno%NUM_XLOGINSERT_LOCKS;MyLockNo=lockToTry;/**TheinsertingAtvalueisinitiallysetto0,aswedon'tknowour*insertlocationyet.*insertingAt值初始化为0,因为我们还不知道我们插入的位置.*/immed=LWLockAcquire(&WALInsertLocks[MyLockNo].l.lock,LW_EXCLUSIVE);if(!immed){/**Ifwecouldn'tgetthelockimmediately,tryanotherlocknext*time.Onasystemwithmoreinsertionlocksthanconcurrent*inserters,thiscausesalltheinserterstoeventuallymigratetoa*lockthatno-oneelseisusing.Onasystemwithmoreinserters*thanlocks,itstillhelpstodistributetheinsertersevenly*acrossthelocks.*如果不能马上获得锁,下回尝试另外一个锁.*在一个insertion锁比并发插入者更多的系统中,*这会导致所有的inserters周期性的迁移到没有使用的锁上面*相反,仍然可以有助于周期性的分发插入者到不同的锁上.*/lockToTry=(lockToTry+1)%NUM_XLOGINSERT_LOCKS;}}//-----------------------------------------------------------WALInsertLockRelease/**Releaseourinsertionlock(orlocks,ifwe'reholdingthemall).*释放insertion锁**NB:Resetallvariablesto0,sotheycauseLWLockWaitForVartoblockthe*nexttimethelockisacquired.*注意:重置所有的变量为0,这样它们可以使LWLockWaitForVar在下一次获取锁时阻塞.*/staticvoidWALInsertLockRelease(void){if(holdingAllLocks)//如持有所有锁{inti;for(i=0;i<NUM_XLOGINSERT_LOCKS;i++)LWLockReleaseClearVar(&WALInsertLocks[i].l.lock,&WALInsertLocks[i].l.insertingAt,0);holdingAllLocks=false;}else{LWLockReleaseClearVar(&WALInsertLocks[MyLockNo].l.lock,&WALInsertLocks[MyLockNo].l.insertingAt,0);}}/**LWLockReleaseClearVar-releaseapreviouslyacquiredlock,resetvariable*LWLockReleaseClearVar-释放先前获取的锁并重置变量*/voidLWLockReleaseClearVar(LWLock*lock,uint64*valptr,uint64val){LWLockWaitListLock(lock);/**Setthevariable'svaluebeforereleasingthelock,thatpreventsrace*araceconditionwhereinanewlockeracquiresthelock,buthasn'tyet*setthevariablesvalue.*在释放锁之前设置变量的值,这可以防止一个新的locker在没有设置变量值的情况下获取锁时的争用.*/*valptr=val;LWLockWaitListUnlock(lock);LWLockRelease(lock);}/**LocktheLWLock'swaitlistagainstconcurrentactivity.*锁定针对并发活动的LWLock等待链表**NB:eventhoughthewaitlistislocked,non-conflictinglockoperations*maystillhappenconcurrently.*注意:虽然等待链表被锁定,非冲突锁操作仍然可能会并发出现**Timespentholdingmutexshouldbeshort!*耗费在持有mutex的时间应该尽可能的短*/staticvoidLWLockWaitListLock(LWLock*lock){uint32old_state;#ifdefLWLOCK_STATSlwlock_stats*lwstats;uint32delays=0;lwstats=get_lwlock_stats_entry(lock);#endifwhile(true){/*alwaystryoncetoacquirelockdirectly*///首次尝试直接获取锁old_state=pg_atomic_fetch_or_u32(&lock->state,LW_FLAG_LOCKED);if(!(old_state&LW_FLAG_LOCKED))break;/*成功获取;gotlock*//*andthenspinwithoutatomicoperationsuntillockisreleased*///然后在没有原子操作的情况下spin,直到锁释放{SpinDelayStatusdelayStatus;//SpinDelay状态init_local_spin_delay(&delayStatus);//初始化while(old_state&LW_FLAG_LOCKED)//获取Lock{perform_spin_delay(&delayStatus);old_state=pg_atomic_read_u32(&lock->state);}#ifdefLWLOCK_STATSdelays+=delayStatus.delays;#endiffinish_spin_delay(&delayStatus);}/**Retry.Thelockmightobviouslyalreadybere-acquiredbythetime*we'reattemptingtogetitagain.*重试,锁有可能在尝试在此获取时已通过重新请求而获得.*/}#ifdefLWLOCK_STATSlwstats->spin_delay_count+=delays;//延迟计数#endif}/**UnlocktheLWLock'swaitlist.*解锁LWLock的等待链表**Notethatitcanbemoreefficienttomanipulateflagsandreleasethe*locksinasingleatomicoperation.*注意,在单个原子操作中操作标志和释放锁可能更有效。*/staticvoidLWLockWaitListUnlock(LWLock*lock){uint32old_statePG_USED_FOR_ASSERTS_ONLY;old_state=pg_atomic_fetch_and_u32(&lock->state,~LW_FLAG_LOCKED);Assert(old_state&LW_FLAG_LOCKED);}/**LWLockRelease-releaseapreviouslyacquiredlock*LWLockRelease-释放先前获取的锁*/voidLWLockRelease(LWLock*lock){LWLockModemode;uint32oldstate;boolcheck_waiters;inti;/**Removelockfromlistoflocksheld.Usually,butnotalways,itwill*bethelatest-acquiredlock;sosearcharraybackwards.*从持有的锁链表中清除锁.*通常来说(但不是总是如此),清除的是最后请求的锁,因此从后往前搜索数组.*/for(i=num_held_lwlocks;--i>=0;)if(lock==held_lwlocks[i].lock)break;if(i<0)elog(ERROR,"lock%sisnotheld",T_NAME(lock));mode=held_lwlocks[i].mode;//模式num_held_lwlocks--;//减一for(;i<num_held_lwlocks;i++)held_lwlocks[i]=held_lwlocks[i+1];PRINT_LWDEBUG("LWLockRelease",lock,mode);/**Releasemyholdonlock,afterthatitcanimmediatelybeacquiredby*others,evenifwestillhavetowakeupotherwaiters.*释放"我"持有的锁,*/if(mode==LW_EXCLUSIVE)oldstate=pg_atomic_sub_fetch_u32(&lock->state,LW_VAL_EXCLUSIVE);elseoldstate=pg_atomic_sub_fetch_u32(&lock->state,LW_VAL_SHARED);/*nobodyelsecanhavethatkindoflock*///舍我其谁!Assert(!(oldstate&LW_VAL_EXCLUSIVE));/**We'restillwaitingforbackendstogetscheduled,don'twakethemup*again.*仍然在等待后台进程获得调度,暂时不需要再次唤醒它们*/if((oldstate&(LW_FLAG_HAS_WAITERS|LW_FLAG_RELEASE_OK))==(LW_FLAG_HAS_WAITERS|LW_FLAG_RELEASE_OK)&&(oldstate&LW_LOCK_MASK)==0)check_waiters=true;elsecheck_waiters=false;/**Aswakingupwaitersrequiresthespinlocktobeacquired,onlydoso*ifnecessary.*因为唤醒等待器需要获取spinlock,所以只有在必要时才这样做。*/if(check_waiters){/*XXX:removebeforecommit?*///XXX:在commit前清除?LOG_LWDEBUG("LWLockRelease",lock,"releasingwaiters");LWLockWakeup(lock);}TRACE_POSTGRESQL_LWLOCK_RELEASE(T_NAME(lock));/**Nowokaytoallowcancel/dieinterrupts.*现在可以允许中断操作了.*/RESUME_INTERRUPTS();}
到此,相信大家对“PostgreSQL中插入数据时与WAL相关的处理逻辑是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。