这篇文章主要介绍“PostgreSQL中ReadBuffer_common函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中ReadBuffer_common函数有什么作用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中ReadBuffer_common函数有什么作用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、数据结构

BufferDesc
共享缓冲区的共享描述符(状态)数据

/**Flagsforbufferdescriptors*buffer描述器标记**Note:TAG_VALIDessentiallymeansthatthereisabufferhashtable*entryassociatedwiththebuffer'stag.*注意:TAG_VALID本质上意味着有一个与缓冲区的标记相关联的缓冲区散列表条目。*///bufferheader锁定#defineBM_LOCKED(1U<<22)/*bufferheaderislocked*///数据需要写入(标记为DIRTY)#defineBM_DIRTY(1U<<23)/*dataneedswriting*///数据是有效的#defineBM_VALID(1U<<24)/*dataisvalid*///已分配buffertag#defineBM_TAG_VALID(1U<<25)/*tagisassigned*///正在R/W#defineBM_IO_IN_PROGRESS(1U<<26)/*readorwriteinprogress*///上一个I/O出现错误#defineBM_IO_ERROR(1U<<27)/*previousI/Ofailed*///开始写则变DIRTY#defineBM_JUST_DIRTIED(1U<<28)/*dirtiedsincewritestarted*///存在等待solepin的其他进程#defineBM_PIN_COUNT_WAITER(1U<<29)/*havewaiterforsolepin*///checkpoint发生,必须刷到磁盘上#defineBM_CHECKPOINT_NEEDED(1U<<30)/*mustwriteforcheckpoint*///持久化buffer(不是unlogged或者初始化fork)#defineBM_PERMANENT(1U<<31)/*permanentbuffer(notunlogged,*orinitfork)*//**BufferDesc--shareddescriptor/statedataforasinglesharedbuffer.*BufferDesc--共享缓冲区的共享描述符(状态)数据**Note:Bufferheaderlock(BM_LOCKEDflag)mustbeheldtoexamineorchange*thetag,stateorwait_backend_pidfields.Ingeneral,bufferheaderlock*isaspinlockwhichiscombinedwithflags,refcountandusagecountinto*singleatomicvariable.Thislayoutallowustodosomeoperationsina*singleatomicoperation,withoutactuallyacquiringandreleasingspinlock;*forinstance,increaseordecreaserefcount.buf_idfieldneverchanges*afterinitialization,sodoesnotneedlocking.freeNextisprotectedby*thebuffer_strategy_locknotbufferheaderlock.TheLWLockcantakecare*ofitself.Thebufferheaderlockis*not*usedtocontrolaccesstothe*datainthebuffer!*注意:必须持有Bufferheader锁(BM_LOCKED标记)才能检查或修改tag/state/wait_backend_pid字段.*通常来说,bufferheaderlock是spinlock,它与标记位/参考计数/使用计数组合到单个原子变量中.*这个布局设计允许我们执行原子操作,而不需要实际获得或者释放spinlock(比如,增加或者减少参考计数).*buf_id字段在初始化后不会出现变化,因此不需要锁定.*freeNext通过buffer_strategy_lock锁而不是bufferheaderlock保护.*LWLock可以很好的处理自己的状态.*务请注意的是:bufferheaderlock不用于控制buffer中的数据访问!**It'sassumedthatnobodychangesthestatefieldwhilebufferheaderlock*isheld.Thusbufferheaderlockholdercandocomplexupdatesofthe*statevariableinsinglewrite,simultaneouslywithlockrelease(cleaning*BM_LOCKEDflag).Ontheotherhand,updatingofstatewithoutholding*bufferheaderlockisrestrictedtoCAS,whichinsurethatBM_LOCKEDflag*isnotset.Atomicincrement/decrement,OR/ANDetc.arenotallowed.*假定在持有bufferheaderlock的情况下,没有人改变状态字段.*持有bufferheaderlock的进程可以执行在单个写操作中执行复杂的状态变量更新,*同步的释放锁(清除BM_LOCKED标记).*换句话说,如果没有持有bufferheaderlock的状态更新,会受限于CAS,*这种情况下确保BM_LOCKED没有被设置.*比如原子的增加/减少(AND/OR)等操作是不允许的.**Anexceptionisthatifwehavethebufferpinned,itstagcan'tchange*underneathus,sowecanexaminethetagwithoutlockingthebufferheader.*Also,inplaceswedoone-timereadsoftheflagswithoutbotheringto*lockthebufferheader;thisisgenerallyforsituationswherewedon't*expecttheflagbitbeingtestedtobechanging.*一种例外情况是如果我们已有bufferpinned,该buffer的tag不能改变(在本进程之下),*因此不需要锁定bufferheader就可以检查tag了.*同时,在执行一次性的flags读取时不需要锁定bufferheader.*这种情况通常用于我们不希望正在测试的flagbit将被改变.**Wecan'tphysicallyremoveitemsfromadiskpageifanotherbackendhas*thebufferpinned.Hence,abackendmayneedtowaitforallotherpins*togoaway.ThisissignaledbystoringitsownPIDinto*wait_backend_pidandsettingflagbitBM_PIN_COUNT_WAITER.Atpresent,*therecanbeonlyonesuchwaiterperbuffer.*如果其他进程有bufferpinned,那么进程不能物理的从磁盘页面中删除items.*因此,后台进程需要等待其他pins清除.这可以通过存储它自己的PID到wait_backend_pid中,*并设置标记位BM_PIN_COUNT_WAITER.*目前,每个缓冲区只能由一个等待进程.**Weusethissamestructforlocalbufferheaders,butthelocksarenot*usedandnotalloftheflagbitsareusefuleither.Toavoidunnecessary*overhead,manipulationsofthestatefieldshouldbedonewithoutactual*atomicoperations(i.e.onlypg_atomic_read_u32()and*pg_atomic_unlocked_write_u32()).*本地缓冲头部使用同样的结构,但并不需要使用locks,而且并不是所有的标记位都使用.*为了避免不必要的负载,状态域的维护不需要实际的原子操作*(比如只有pg_atomic_read_u32()andpg_atomic_unlocked_write_u32())**Becarefultoavoidincreasingthesizeofthestructwhenaddingor*reorderingmembers.Keepingitbelow64bytes(themostcommonCPU*cachelinesize)isfairlyimportantforperformance.*在增加或者记录成员变量时,小心避免增加结构体的大小.*保持结构体大小在64字节内(通常的CPU缓存线大小)对于性能是非常重要的.*/typedefstructBufferDesc{//buffertagBufferTagtag;/*IDofpagecontainedinbuffer*///buffer索引编号(0开始)intbuf_id;/*buffer'sindexnumber(from0)*//*stateofthetag,containingflags,refcountandusagecount*///tag状态,包括flags/refcount和usagecountpg_atomic_uint32state;//pin-count等待进程IDintwait_backend_pid;/*backendPIDofpin-countwaiter*///空闲链表链中下一个空闲的bufferintfreeNext;/*linkinfreelistchain*///缓冲区内容锁LWLockcontent_lock;/*tolockaccesstobuffercontents*/}BufferDesc;

BufferTag
Buffer tag标记了buffer存储的是磁盘中哪个block

/**Buffertagidentifieswhichdiskblockthebuffercontains.*Buffertag标记了buffer存储的是磁盘中哪个block**Note:theBufferTagdatamustbesufficienttodeterminewheretowritethe*block,withoutreferencetopg_classorpg_tablespaceentries.It's*possiblethatthebackendflushingthebufferdoesn'tevenbelievethe*relationisvisibleyet(itsxactmayhavestartedbeforethexactthat*createdtherel).Thestoragemanagermustbeabletocopeanyway.*注意:BufferTag必须足以确定如何写block而不需要参照pg_class或者pg_tablespace数据字典信息.*有可能后台进程在刷新缓冲区的时候深圳不相信关系是可见的(事务可能在创建rel的事务之前).*存储管理器必须可以处理这些事情.**Note:ifthere'sanypadbytesinthestruct,INIT_BUFFERTAGwillhave*tobefixedtozerothem,sincethisstructisusedasahashkey.*注意:如果在结构体中有填充的字节,INIT_BUFFERTAG必须将它们固定为零,因为这个结构体用作散列键.*/typedefstructbuftag{//物理relation标识符RelFileNodernode;/*physicalrelationidentifier*/ForkNumberforkNum;//相对于relation起始的块号BlockNumberblockNum;/*blknumrelativetobeginofreln*/}BufferTag;二、源码解读

ReadBuffer_common函数是所有ReadBuffer相关的通用逻辑,其实现逻辑如下:
1.初始化相关变量和执行相关判断(是否扩展isExtend?是否临时表isLocalBuf?)
2.如为临时表,则调用LocalBufferAlloc获取描述符;否则调用BufferAlloc获取描述符;
同时,设置是否在缓存命中的标记(变量found)
3.如在缓存中命中
3.1如非扩展buffer,更新统计信息,如有需要,锁定buffer并返回
3.2如为扩展buffer,则获取block
3.2.1如PageIsNew返回F,则报错
3.2.2如为本地buffer(临时表),则调整标记
3.2.3如非本地buffer,则清除BM_VALID标记
4.没有在缓存中命中,则获取block
4.1如为扩展buffer,通过填充0初始化buffer,调用smgrextend扩展
4.2如为普通buffer
4.2.1如模式为RBM_ZERO_AND_LOCK/RBM_ZERO_AND_CLEANUP_LOCK,填充0
4.2.2否则,通过smgr(存储管理器)读取block,如需要,则跟踪I/O时间,同时检查垃圾数据
5.已扩展了buffer或者已读取了block
5.1如需要,锁定buffer
5.2如为临时表,则调整标记;否则设置BM_VALID,中断IO,唤醒等待的进程
5.3更新统计信息
5.4返回buffer

/**ReadBuffer_common--commonlogicforallReadBuffervariants*ReadBuffer_common--所有ReadBuffer相关的通用逻辑***hitissettotrueiftherequestwassatisfiedfromsharedbuffercache.**hit设置为T,如sharedbuffer中已存在此buffer*/staticBufferReadBuffer_common(SMgrRelationsmgr,charrelpersistence,ForkNumberforkNum,BlockNumberblockNum,ReadBufferModemode,BufferAccessStrategystrategy,bool*hit){BufferDesc*bufHdr;//buffer描述符BlockbufBlock;//相应的blockboolfound;//是否命中?boolisExtend;//扩展?boolisLocalBuf=SmgrIsTemp(smgr);//本地buffer?*hit=false;/*Makesurewewillhaveroomtorememberthebufferpin*///确保有空间存储bufferpinResourceOwnerEnlargeBuffers(CurrentResourceOwner);//如为P_NEW,则需扩展isExtend=(blockNum==P_NEW);//跟踪TRACE_POSTGRESQL_BUFFER_READ_START(forkNum,blockNum,smgr->smgr_rnode.node.spcNode,smgr->smgr_rnode.node.dbNode,smgr->smgr_rnode.node.relNode,smgr->smgr_rnode.backend,isExtend);/*SubstituteproperblocknumberifcalleraskedforP_NEW*///如调用方要求P_NEW,则替换适当的块号if(isExtend)blockNum=smgrnblocks(smgr,forkNum);if(isLocalBuf){//本地buffer(临时表)bufHdr=LocalBufferAlloc(smgr,forkNum,blockNum,&found);if(found)pgBufferUsage.local_blks_hit++;elseif(isExtend)pgBufferUsage.local_blks_written++;elseif(mode==RBM_NORMAL||mode==RBM_NORMAL_NO_LOG||mode==RBM_ZERO_ON_ERROR)pgBufferUsage.local_blks_read++;}else{//非临时表/**lookupthebuffer.IO_IN_PROGRESSissetiftherequestedblockis*notcurrentlyinmemory.*搜索buffer.*如请求的block不在内存中,则IO_IN_PROGRESS设置为T*///获取buffer描述符bufHdr=BufferAlloc(smgr,relpersistence,forkNum,blockNum,strategy,&found);if(found)//在内存中命中pgBufferUsage.shared_blks_hit++;elseif(isExtend)//新的bufferpgBufferUsage.shared_blks_written++;elseif(mode==RBM_NORMAL||mode==RBM_NORMAL_NO_LOG||mode==RBM_ZERO_ON_ERROR)//读取blockpgBufferUsage.shared_blks_read++;}/*AtthispointwedoNOTholdanylocks.*///这时候,我们还没有持有任何锁./*ifitwasalreadyinthebufferpool,we'redone*///----------如果buffer已在换冲池中,工作已完成if(found){//-------------buffer已在缓冲池中//已在换冲池中if(!isExtend){//非扩展buffer/*Justneedtoupdatestatsbeforeweexit*///在退出前,更新统计信息*hit=true;VacuumPageHit++;if(VacuumCostActive)VacuumCostBalance+=VacuumCostPageHit;TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum,blockNum,smgr->smgr_rnode.node.spcNode,smgr->smgr_rnode.node.dbNode,smgr->smgr_rnode.node.relNode,smgr->smgr_rnode.backend,isExtend,found);/**InRBM_ZERO_AND_LOCKmodethecallerexpectsthepagetobe*lockedonreturn.*RBM_ZERO_AND_LOCK模式,调用者期望page锁定后才返回*/if(!isLocalBuf){//非临时表bufferif(mode==RBM_ZERO_AND_LOCK)LWLockAcquire(BufferDescriptorGetContentLock(bufHdr),LW_EXCLUSIVE);elseif(mode==RBM_ZERO_AND_CLEANUP_LOCK)LockBufferForCleanup(BufferDescriptorGetBuffer(bufHdr));}//根据buffer描述符读取buffer并返回buffer//#defineBufferDescriptorGetBuffer(bdesc)((bdesc)->buf_id+1)returnBufferDescriptorGetBuffer(bufHdr);}/**Wegethereonlyinthecornercasewherewearetryingtoextend*therelationbutwefoundapre-existingbuffermarkedBM_VALID.*Thiscanhappenbecausemdreaddoesn'tcomplainaboutreadsbeyond*EOF(whenzero_damaged_pagesisON)andsoapreviousattemptto*readablockbeyondEOFcouldhavelefta"valid"zero-filled*buffer.Unfortunately,wehavealsoseenthiscaseoccurring*becauseofbuggyLinuxkernelsthatsometimesreturnan*lseek(SEEK_END)resultthatdoesn'taccountforarecentwrite.In*thatsituation,thepre-existingbufferwouldcontainvaliddata*thatwedon'twanttooverwrite.Sincethelegitimatecaseshould*alwayshaveleftazero-filledbuffer,complainifnotPageIsNew.*程序执行来到这里,进程尝试扩展relation但发现了先前已存在的标记为BM_VALID的buffer.*这种情况之所以发生是因为mdread对于在EOF之后的读不会报错(zero_damaged_pages设置为ON),*并且先前尝试读取EOF的block遗留了"valid"的已初始化(填充0)的buffer.*不幸的是,我们同样发现因为Linux内核的bug(有时候会返回lseek/SEEK_END结果)导致这种情况.*在这种情况下,先前已存在的buffer会存储有效的数据,这些数据不希望被覆盖.*由于合法的情况下应该总是留下一个零填充的缓冲区,如果不是PageIsNew,则报错。*///获取blockbufBlock=isLocalBuf?LocalBufHdrGetBlock(bufHdr):BufHdrGetBlock(bufHdr);if(!PageIsNew((Page)bufBlock))//不是PageIsNew,则报错ereport(ERROR,(errmsg("unexpecteddatabeyondEOFinblock%uofrelation%s",blockNum,relpath(smgr->smgr_rnode,forkNum)),errhint("Thishasbeenseentooccurwithbuggykernels;considerupdatingyoursystem.")));/**We*must*dosmgrextendbeforesucceeding,elsethepagewillnot*bereservedbythekernel,andthenextP_NEWcallwilldecideto*returnthesamepage.CleartheBM_VALIDbit,dotheStartBufferIO*callthatBufferAllocdidn't,andproceed.*在成功执行前,必须执行smgrextend,否则的话page不能被内核保留,*同时下一个P_NEW调用会确定返回同样的page.*清除BM_VALID位,执行BufferAlloc没有执行的StartBufferIO调用,然后继续。*/if(isLocalBuf){//临时表/*Onlyneedtoadjustflags*///只需要调整标记uint32buf_state=pg_atomic_read_u32(&bufHdr->state);Assert(buf_state&BM_VALID);buf_state&=~BM_VALID;pg_atomic_unlocked_write_u32(&bufHdr->state,buf_state);}else{//非临时表/**Looptohandletheverysmallpossibilitythatsomeonere-sets*BM_VALIDbetweenourclearingitandStartBufferIOinspecting*it.*循环,直至StartBufferIO返回T为止*/do{uint32buf_state=LockBufHdr(bufHdr);Assert(buf_state&BM_VALID);//清除BM_VALID标记buf_state&=~BM_VALID;UnlockBufHdr(bufHdr,buf_state);}while(!StartBufferIO(bufHdr,true));}}//-------------buffer不在缓冲池中/**ifwehavegottentothispoint,wehaveallocatedabufferforthe*pagebutitscontentsarenotyetvalid.IO_IN_PROGRESSissetforit,*ifit'sasharedbuffer.*如果到了这个份上,我们已经为page分配了buffer,但其中的内容还没有生效.*如果是共享内存,那么设置IO_IN_PROGRESS标记.**Note:ifsmgrextendfails,wewillendupwithabufferthatis*allocatedbutnotmarkedBM_VALID.P_NEWwillstillselectthesame*blocknumber(becausetherelationdidn'tgetanylongerondisk)and*sofutureattemptstoextendtherelationwillfindthesamebuffer(if*it'snotbeenrecycled)butcomerightbackheretotrysmgrextend*again.*注意:如果smgrextend失败,我们将以一个已分配但为设置为BM_VALID的buffer结束这次调用*///验证Assert(!(pg_atomic_read_u32(&bufHdr->state)&BM_VALID));/*spinlocknotneeded*///获取blockbufBlock=isLocalBuf?LocalBufHdrGetBlock(bufHdr):BufHdrGetBlock(bufHdr);if(isExtend){//--------扩展block/*newbuffersarezero-filled*///新buffers使用0填充MemSet((char*)bufBlock,0,BLCKSZ);/*don'tsetchecksumforall-zeropage*///对于使用全0填充的page,不要设置checksumsmgrextend(smgr,forkNum,blockNum,(char*)bufBlock,false);/**NB:we're*not*doingaScheduleBufferTagForWritebackhere;*althoughwe'reessentiallyperformingawrite.Atleastonlinux*doingsodefeatsthe'delayedallocation'mechanism,leadingto*increasedfilefragmentation.*注意:这里我们不会执行ScheduleBufferTagForWriteback.虽然我们实质上正在执行写操作.*起码,在Linux平台,执行这个操作会破坏“延迟分配”机制,导致文件碎片.*/}else{//--------普通block/**Readinthepage,unlessthecallerintendstooverwriteitand*justwantsustoallocateabuffer.*读取page,除非调用者期望覆盖它并且希望我们分配buffer.**/if(mode==RBM_ZERO_AND_LOCK||mode==RBM_ZERO_AND_CLEANUP_LOCK)//如为RBM_ZERO_AND_LOCK或者RBM_ZERO_AND_CLEANUP_LOCK模式,初始化为0MemSet((char*)bufBlock,0,BLCKSZ);else{//其他模式instr_timeio_start,//io的起止时间io_time;if(track_io_timing)INSTR_TIME_SET_CURRENT(io_start);//smgr(存储管理器)读取blocksmgrread(smgr,forkNum,blockNum,(char*)bufBlock);if(track_io_timing){//需要跟踪io时间INSTR_TIME_SET_CURRENT(io_time);INSTR_TIME_SUBTRACT(io_time,io_start);pgstat_count_buffer_read_time(INSTR_TIME_GET_MICROSEC(io_time));INSTR_TIME_ADD(pgBufferUsage.blk_read_time,io_time);}/*checkforgarbagedata*///检查垃圾数据if(!PageIsVerified((Page)bufBlock,blockNum)){//如果page为通过验证if(mode==RBM_ZERO_ON_ERROR||zero_damaged_pages){//出错,则初始化ereport(WARNING,(errcode(ERRCODE_DATA_CORRUPTED),errmsg("invalidpageinblock%uofrelation%s;zeroingoutpage",blockNum,relpath(smgr->smgr_rnode,forkNum))));//初始化MemSet((char*)bufBlock,0,BLCKSZ);}else//出错,报错ereport(ERROR,(errcode(ERRCODE_DATA_CORRUPTED),errmsg("invalidpageinblock%uofrelation%s",blockNum,relpath(smgr->smgr_rnode,forkNum))));}}}//---------已扩展了buffer或者已读取了block/**InRBM_ZERO_AND_LOCKmode,grabthebuffercontentlockbeforemarking*thepageasvalid,tomakesurethatnootherbackendseesthezeroed*pagebeforethecallerhashadachancetoinitializeit.*在RBM_ZERO_AND_LOCK模式下,在标记page为有效之前获取buffercontentlock,*确保在调用者初始化之前没有其他进程看到已初始化为0的page**Sinceno-oneelsecanbelookingatthepagecontentsyet,thereisno*differencebetweenanexclusivelockandacleanup-strengthlock.(Note*thatwecannotuseLockBuffer()orLockBufferForCleanup()here,because*theyassertthatthebufferisalreadyvalid.)*由于没有其他进程可以搜索page内容,因此获取独占锁和cleanup-strength锁没有区别.*(注意不能在这里使用LockBuffer()或者LockBufferForCleanup(),因为这些函数假定buffer有效)*/if((mode==RBM_ZERO_AND_LOCK||mode==RBM_ZERO_AND_CLEANUP_LOCK)&&!isLocalBuf){//锁定LWLockAcquire(BufferDescriptorGetContentLock(bufHdr),LW_EXCLUSIVE);}if(isLocalBuf){//临时表/*Onlyneedtoadjustflags*///只需要调整标记uint32buf_state=pg_atomic_read_u32(&bufHdr->state);buf_state|=BM_VALID;pg_atomic_unlocked_write_u32(&bufHdr->state,buf_state);}else{//普通表/*SetBM_VALID,terminateIO,andwakeupanywaiters*///设置BM_VALID,中断IO,唤醒等待的进程TerminateBufferIO(bufHdr,false,BM_VALID);}//更新统计信息VacuumPageMiss++;if(VacuumCostActive)VacuumCostBalance+=VacuumCostPageMiss;//跟踪TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum,blockNum,smgr->smgr_rnode.node.spcNode,smgr->smgr_rnode.node.dbNode,smgr->smgr_rnode.node.relNode,smgr->smgr_rnode.backend,isExtend,found);//返回buffer//#defineBufferDescriptorGetBuffer(bdesc)((bdesc)->buf_id+1)returnBufferDescriptorGetBuffer(bufHdr);}三、跟踪分析

测试场景一:Block不在缓冲区中
脚本:

16:42:48(xdb@[local]:5432)testdb=#select*fromt1limit10;

启动gdb,设置断点

(gdb)bReadBuffer_commonBreakpoint1at0x876e28:filebufmgr.c,line711.(gdb)cContinuing.Breakpoint1,ReadBuffer_common(smgr=0x2b7cce0,relpersistence=112'p',forkNum=MAIN_FORKNUM,blockNum=0,mode=RBM_NORMAL,strategy=0x0,hit=0x7ffc7761dfab)atbufmgr.c:711711boolisLocalBuf=SmgrIsTemp(smgr);(gdb)

1.初始化相关变量和执行相关判断(是否扩展isExtend?是否临时表isLocalBuf?)

(gdb)n713*hit=false;(gdb)716ResourceOwnerEnlargeBuffers(CurrentResourceOwner);(gdb)718isExtend=(blockNum==P_NEW);(gdb)720TRACE_POSTGRESQL_BUFFER_READ_START(forkNum,blockNum,(gdb)728if(isExtend)(gdb)731if(isLocalBuf)(gdb)745bufHdr=BufferAlloc(smgr,relpersistence,forkNum,blockNum,(gdb)

2.调用BufferAlloc获取buffer描述符

(gdb)747if(found)(gdb)p*bufHdr$1={tag={rnode={spcNode=1663,dbNode=16402,relNode=51439},forkNum=MAIN_FORKNUM,blockNum=0},buf_id=108,state={value=2248409089},wait_backend_pid=0,freeNext=-2,content_lock={tranche=54,state={value=536870912},waiters={head=2147483647,tail=2147483647}}}(gdb)pfound$2=false(gdb)(gdb)n750pgBufferUsage.shared_blks_read++;-->更新统计信息(gdb)

4.没有在缓存中命中,则获取block

756if(found)(gdb)856Assert(!(pg_atomic_read_u32(&bufHdr->state)&BM_VALID));/*spinlocknotneeded*/(gdb)858bufBlock=isLocalBuf?LocalBufHdrGetBlock(bufHdr):BufHdrGetBlock(bufHdr);(gdb)860if(isExtend)(gdb)pbufBlock$4=(Block)0x7fe8c240e380

4.2如为普通buffer
4.2.1如模式为RBM_ZERO_AND_LOCK/RBM_ZERO_AND_CLEANUP_LOCK,填充0
4.2.2否则,通过smgr(存储管理器)读取block,如需要,则跟踪I/O时间,同时检查垃圾数据

(gdb)pmode$5=RBM_NORMAL(gdb)(gdb)n880if(mode==RBM_ZERO_AND_LOCK||mode==RBM_ZERO_AND_CLEANUP_LOCK)(gdb)887if(track_io_timing)(gdb)890smgrread(smgr,forkNum,blockNum,(char*)bufBlock);(gdb)892if(track_io_timing)(gdb)p*smgr$6={smgr_rnode={node={spcNode=1663,dbNode=16402,relNode=51439},backend=-1},smgr_owner=0x7fe8ee2bc7b8,smgr_targblock=4294967295,smgr_fsm_nblocks=4294967295,smgr_vm_nblocks=4294967295,smgr_which=0,md_num_open_segs={1,0,0,0},md_seg_fds={0x2b0dd78,0x0,0x0,0x0},next_unowned_reln=0x0}(gdb)pforkNum$7=MAIN_FORKNUM(gdb)pblockNum$8=0(gdb)p(char*)bufBlock$9=0x7fe8c240e380"\001"(gdb)

5.已扩展了buffer或者已读取了block
5.1如需要,锁定buffer
5.2如为临时表,则调整标记;否则设置BM_VALID,中断IO,唤醒等待的进程

(gdb)n901if(!PageIsVerified((Page)bufBlock,blockNum))(gdb)932if((mode==RBM_ZERO_AND_LOCK||mode==RBM_ZERO_AND_CLEANUP_LOCK)&&(gdb)n938if(isLocalBuf)(gdb)949TerminateBufferIO(bufHdr,false,BM_VALID);(gdb)

5.3更新统计信息
5.4返回buffer

(gdb)952VacuumPageMiss++;(gdb)953if(VacuumCostActive)(gdb)956TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum,blockNum,(gdb)964returnBufferDescriptorGetBuffer(bufHdr);(gdb)965}(gdb)

buf为109

(gdb)ReadBufferExtended(reln=0x7fe8ee2bc7a8,forkNum=MAIN_FORKNUM,blockNum=0,mode=RBM_NORMAL,strategy=0x0)atbufmgr.c:666666if(hit)(gdb)668returnbuf;(gdb)pbuf$10=109(gdb)

测试场景二:Block已在缓冲区中
再次执行上面的SQL语句,这时候相应的block已读入到buffer中

(gdb)delDeleteallbreakpoints?(yorn)y(gdb)cContinuing.^CProgramreceivedsignalSIGINT,Interrupt.0x00007fe8ec448903in__epoll_wait_nocancel()at../sysdeps/unix/syscall-template.S:8181T_PSEUDO(SYSCALL_SYMBOL,SYSCALL_NAME,SYSCALL_NARGS)(gdb)bReadBuffer_commonBreakpoint2at0x876e28:filebufmgr.c,line711.(gdb)

found变量为T

...(gdb)745bufHdr=BufferAlloc(smgr,relpersistence,forkNum,blockNum,(gdb)747if(found)(gdb)pfound$11=true(gdb)(gdb)n748pgBufferUsage.shared_blks_hit++;(gdb)

进入相应的逻辑
3.如在缓存中命中
3.1如非扩展buffer,更新统计信息,如有需要,锁定buffer并返回
3.2如为扩展buffer,则获取block
3.2.1如PageIsNew返回F,则报错
3.2.2如为本地buffer(临时表),则调整标记
3.2.3如非本地buffer,则清除BM_VALID标记

(gdb)756if(found)(gdb)758if(!isExtend)(gdb)761*hit=true;(gdb)762VacuumPageHit++;(gdb)764if(VacuumCostActive)(gdb)767TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum,blockNum,(gdb)779if(!isLocalBuf)(gdb)781if(mode==RBM_ZERO_AND_LOCK)(gdb)784elseif(mode==RBM_ZERO_AND_CLEANUP_LOCK)(gdb)788returnBufferDescriptorGetBuffer(bufHdr);(gdb)965}(gdb)

到此,关于“PostgreSQL中ReadBuffer_common函数有什么作用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!