PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用
本篇内容介绍了“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
一、数据结构FSMAddress
内部的FSM处理过程以逻辑地址scheme的方式工作,树的每一个层次都可以认为是一个独立的地址文件.
/**TheinternalFSMroutinesworkonalogicaladdressingscheme.Each*levelofthetreecanbethoughtofasaseparatelyaddressablefile.*内部的FSM处理过程工作在一个逻辑地址scheme上.*树的每一个层次都可以认为是一个独立的地址文件.*/typedefstruct{//层次intlevel;/*level*///该层次内的页编号intlogpageno;/*pagenumberwithinthelevel*/}FSMAddress;/*Addressoftherootpage.*///根页地址staticconstFSMAddressFSM_ROOT_ADDRESS={FSM_ROOT_LEVEL,0};
FSMPage
FSM page数据结构.详细可参看src/backend/storage/freespace/README.
/**StructureofaFSMpage.Seesrc/backend/storage/freespace/READMEfor*details.*FSMpage数据结构.详细可参看src/backend/storage/freespace/README.*/typedefstruct{/**fsm_search_avail()triestospreadtheloadofmultiplebackendsby*returningdifferentpagestodifferentbackendsinaround-robin*fashion.fp_next_slotpointstothenextslottobereturned(assuming*there'senoughspaceonitfortherequest).It'sdefinedasanint,*becauseit'supdatedwithoutanexclusivelock.uint16wouldbemore*appropriate,butintismorelikelytobeatomically*fetchable/storable.*fsm_search_avail()函数尝试通过在一轮循环中返回不同的页面到不同的后台进程,*从而分散在后台进程上分散负载.*该字段因为无需独占锁,因此定义为整型.*unit16可能会更合适,但整型看起来更适合于原子提取和存储.*/intfp_next_slot;/**fp_nodescontainsthebinarytree,storedinarray.Thefirst*NonLeafNodesPerPageelementsareuppernodes,andthefollowing*LeafNodesPerPageelementsareleafnodes.Unusednodesarezero.*fp_nodes以数组的形式存储二叉树.*第一个NonLeafNodesPerPage元素是上一层的节点,接下来的LeafNodesPerPage元素是叶子节点.*未使用的节点为0.*/uint8fp_nodes[FLEXIBLE_ARRAY_MEMBER];}FSMPageData;typedefFSMPageData*FSMPage;
FSMLocalMap
对于小表,不需要创建FSM来存储空间信息,使用本地的内存映射信息.
/*Eitheralreadytried,orbeyondtheendoftherelation*///已尝试或者已在表的末尾之后#defineFSM_LOCAL_NOT_AVAIL0x00/*Availabletotry*///可用于尝试#defineFSM_LOCAL_AVAIL0x01/**Forsmallrelations,wedon'tcreateFSMtosavespace,insteadweuse*localin-memorymapofpagestotry.Tolocatefreespace,wesimplytry*pagesdirectlywithoutknowingaheadoftimehowmuchfreespacetheyhave.*对于小表,不需要创建FSM来存储空间信息,使用本地的内存映射信息.*为了定位空闲空间,我们不需要知道他们有多少空闲空间而是直接简单的对page进行尝试.**Notethatthismapisusedtothefindtheblockwithrequiredfreespace*foranygivenrelation.Weclearthismapwhenwehavefoundablockwith*enoughfreespace,whenweextendtherelation,orontransactionabort.*Seesrc/backend/storage/freespace/READMEforfurtherdetails.*注意这个map用于搜索给定表的请求空闲空间.*在找到有足够空闲空间的block/扩展了relation/在事务回滚时,则清除这个map的信息.*详细可查看src/backend/storage/freespace/README.*/typedefstruct{BlockNumbernblocks;//块数uint8map[HEAP_FSM_CREATION_THRESHOLD];//数组}FSMLocalMap;staticFSMLocalMapfsm_local_map={0,{FSM_LOCAL_NOT_AVAIL}};#defineFSM_LOCAL_MAP_EXISTS(fsm_local_map.nblocks>0)二、源码解读
RecordAndGetPageWithFreeSpace返回满足条件的block,其主要逻辑如下:
1.初始化相关变量
2.如存在本地map,则首先使用该文件,调用fsm_local_search
3.如果没有本地map也没有FSM,创建本地map,然后调用fsm_local_search
4.使用FSM搜索
4.1获取FSM中原page可用空间对应的catalog
4.2根据所需空间大小,获取FSM中相应的catalog
4.3根据原页面,获取heap block所在的位置(FSMAddress)
4.4检索获取目标slot
4.5如目标slot合法,则获取相应的block,否则使用fsm_search搜索合适的block
/**RecordAndGetPageWithFreeSpace-updateinfoaboutapageandtryagain.*RecordAndGetPageWithFreeSpace-更新pageinfo并再次尝试.**Weprovidethiscomboformtosavesomelockingoverhead,comparedto*separateRecordPageWithFreeSpace+GetPageWithFreeSpacecalls.There's*alsosomeefforttoreturnapageclosetotheoldpage;ifthere'sa*pagewithenoughfreespaceonthesameFSMpagewheretheoldonepage*islocated,itispreferred.*相对于单独的RecordPageWithFreeSpace+GetPageWithFreeSpace调用,*我们提供这个组合形式用于节省一些锁的负载.*这里同样存储一些努力用于返回接近旧page的page.*如果与旧的page在同一个FSMpage上有足够空闲空间的page存在,那这个page会被选中.**Forverysmallheaprelationsthatdon'thaveaFSM,weupdatethelocal*maptoindicatewehavetriedapage,andreturnthenextpagetotry.*对于非常小的堆表,是不需要FSM的,直接更新本地map来提示进程需要尝试获得一个page,并返回下一个page.*/BlockNumberRecordAndGetPageWithFreeSpace(Relationrel,BlockNumberoldPage,SizeoldSpaceAvail,SizespaceNeeded){intold_cat;intsearch_cat;FSMAddressaddr;//FSM地址uint16slot;//槽号intsearch_slot;BlockNumbernblocks=InvalidBlockNumber;/*Firsttrythelocalmap,ifitexists.*///如存在本地map,则首先使用该文件.//#defineFSM_LOCAL_MAP_EXISTS(fsm_local_map.nblocks>0)if(FSM_LOCAL_MAP_EXISTS){Assert((rel->rd_rel->relkind==RELKIND_RELATION||rel->rd_rel->relkind==RELKIND_TOASTVALUE)&&fsm_local_map.map[oldPage]==FSM_LOCAL_AVAIL);//设置oldPage为不可用fsm_local_map.map[oldPage]=FSM_LOCAL_NOT_AVAIL;//搜索并返回结果returnfsm_local_search();}if(!fsm_allow_writes(rel,oldPage,InvalidBlockNumber,&nblocks)){//----如果FSM不允许写/**IfwehaveneitheralocalmapnoraFSM,weprobablyjusttried*thetargetblockinthesmgrrelationentryandfailed,sowe'll*needtocreatethelocalmap.*如果没有本地map也没有FSM,*那么我们只是尝试了smgrrelation中的目标block而且失败了,那么需要创建本地map.*///设置本地mapfsm_local_set(rel,nblocks);//搜索本地mapreturnfsm_local_search();}/*NormalFSMlogicfollows*///------使用FSM的逻辑//oldSpaceAvail/32,最大255/254old_cat=fsm_space_avail_to_cat(oldSpaceAvail);//(needed+FSM_CAT_STEP-1)/FSM_CAT_STEP//#defineFSM_CAT_STEP(BLCKSZ/FSM_CATEGORIES)//#defineFSM_CATEGORIES256search_cat=fsm_space_needed_to_cat(spaceNeeded);/*GetthelocationoftheFSMbyterepresentingtheheapblock*///获得对应heapblock的位置addr=fsm_get_location(oldPage,&slot);//在给定的FSMpage和slot中设置值,并返回slotsearch_slot=fsm_set_and_search(rel,addr,slot,old_cat,search_cat);/**Iffsm_set_and_searchfoundasuitablenewblock,returnthat.*Otherwise,searchasusual.*如fsm_set_and_search成功找到合适的block,则返回;否则,执行常规的检索.*/if(search_slot!=-1)returnfsm_get_heap_blk(addr,search_slot);elsereturnfsm_search(rel,search_cat);}/**Searchthelocalmapforanavailableblocktotry,indescendingorder.*Assuch,thereisnoheuristicavailabletodecidewhichorderwillbe*bettertotry,buttheprobabilityofhavingspaceinthelastblockinthe*mapishigherbecausethatisthemostrecentblockaddedtotheheap.*以倒序的方式检索本地map找可用的block.*在这种情况下,没有特别好的办法用于确定那种排序方法更好,*但在map中最后一个block中存在空闲空间的可能性更高,因为这是最近添加到堆中的block.**ThisfunctionisusedwhenthereisnoFSM.*如无FSM则使用该函数.*/staticBlockNumberfsm_local_search(void){BlockNumbertarget_block;/*Localmapmustbesetbynow.*///现在本地map必须已设置Assert(FSM_LOCAL_MAP_EXISTS);//目标blocktarget_block=fsm_local_map.nblocks;do{//循环target_block--;//从最后一个block开始if(fsm_local_map.map[target_block]==FSM_LOCAL_AVAIL)returntarget_block;//最后一个block可用,则返回}while(target_block>0);//target_block==0/**Ifwedidn'tfindanyavailableblocktotryinthelocalmap,then*clearit.Thispreventsusfromusingthemapagainwithoutsettingit*first,whichwouldotherwiseleadtothesameconclusionagainand*again.*在本地map中没有发现可用的block,则清除相关信息.*这可以防止我们在没有正确设置map的情况下使用该map,*这会导致重复的相同结论(没有可用的block).*/FSMClearLocalMap();//返回InvalidBlockNumberreturnInvalidBlockNumber;}/**Initializeorupdatethelocalmapofblockstotry,forwhenthereis*noFSM.*如无FSM,则初始化并更新本地map**Whenweinitializethemap,thewholeheapispotentiallyavailableto*try.Testingrevealedthattryingeveryblockcancauseasmall*performancedipcomparedtowhenweuseaFSM,sowetryeveryother*blockinstead.*在我们初始化map的时候,整个堆可能已可用.*测试表名,与使用FSM相比,尝试每个块会导致小幅的性能下降,因此尝试每一个块.*/staticvoidfsm_local_set(Relationrel,BlockNumbercur_nblocks){BlockNumberblkno,cached_target_block;/*Thelocalmapmustnotbesetalready.*///验证Assert(!FSM_LOCAL_MAP_EXISTS);/**Startingatthecurrentlastblockintherelationandworking*backwards,markalternatingblocksasavailable.*在关系的当前最后一个块开始往后减少,标记可更新的块可用.*/blkno=cur_nblocks-1;//最后一个块while(true){//更新为可用fsm_local_map.map[blkno]=FSM_LOCAL_AVAIL;if(blkno>=2)blkno-=2;elsebreak;}/*Cachethenumberofblocks.*///缓存块数fsm_local_map.nblocks=cur_nblocks;/*Setthestatusofthecachedtargetblockto'unavailable'.*///设置缓存的目标块状态为未可用cached_target_block=RelationGetTargetBlock(rel);if(cached_target_block!=InvalidBlockNumber&&cached_target_block<cur_nblocks)fsm_local_map.map[cached_target_block]=FSM_LOCAL_NOT_AVAIL;}/**Returncategorycorrespondingxbytesoffreespace*返回相应有x字节空间空间的目录*/staticuint8fsm_space_avail_to_cat(Sizeavail){intcat;//确保请求的小于块大小Assert(avail<BLCKSZ);//如大于最大请求大小,返回255//#defineMaxFSMRequestSizeMaxHeapTupleSize//#defineMaxHeapTupleSize(BLCKSZ-MAXALIGN(SizeOfPageHeaderData+sizeof(ItemIdData)))if(avail>=MaxFSMRequestSize)return255;//#defineFSM_CAT_STEP(BLCKSZ/FSM_CATEGORIES)//#defineFSM_CATEGORIES256//块大小为8K则FSM_CAT_STEP=32cat=avail/FSM_CAT_STEP;/**Thehighestcategory,255,isreservedforMaxFSMRequestSizebytesor*more.*最高层的目录,255,保留用于MaxFSMRequestSize或者更大的大小.*/if(cat>254)cat=254;//返回254return(uint8)cat;}/**Whichcategorydoesapageneedtohave,toaccommodatexbytesofdata?*Whilefsm_size_to_avail_cat()roundsdown,thisneedstoroundup.*哪一个目录有需要的page,可满足xbytes大小的数据.*因为fsm_size_to_avail_cat()往下取整,因此这里需要往上取整.*/staticuint8fsm_space_needed_to_cat(Sizeneeded){intcat;/*Can'taskformorespacethanthehighestcategoryrepresents*///不能要求最大目录可能表示的空间大小if(needed>MaxFSMRequestSize)elog(ERROR,"invalidFSMrequestsize%zu",needed);if(needed==0)return1;cat=(needed+FSM_CAT_STEP-1)/FSM_CAT_STEP;if(cat>255)cat=255;return(uint8)cat;}/**ReturntheFSMlocationcorrespondingtogivenheapblock.*返回给定堆block的FSM位置.*///addr=fsm_get_location(oldPage,&slot);staticFSMAddressfsm_get_location(BlockNumberheapblk,uint16*slot){FSMAddressaddr;addr.level=FSM_BOTTOM_LEVEL;//#defineSlotsPerFSMPageLeafNodesPerPage//#defineLeafNodesPerPage(NodesPerPage-NonLeafNodesPerPage)//#defineNodesPerPage(BLCKSZ-MAXALIGN(SizeOfPageHeaderData)-\offsetof(FSMPageData,fp_nodes))//#defineNonLeafNodesPerPage(BLCKSZ/2-1)addr.logpageno=heapblk/SlotsPerFSMPage;*slot=heapblk%SlotsPerFSMPage;returnaddr;}三、跟踪分析
测试脚本
15:54:13(xdb@[local]:5432)testdb=#insertintot1values(1,'1','1');
启动gdb,设置断点
(gdb)bRecordAndGetPageWithFreeSpaceBreakpoint1at0x8879e4:filefreespace.c,line152.(gdb)cContinuing.Breakpoint1,RecordAndGetPageWithFreeSpace(rel=0x7fad0df13788,oldPage=1,oldSpaceAvail=16,spaceNeeded=32)atfreespace.c:152152intold_cat=fsm_space_avail_to_cat(oldSpaceAvail);(gdb)
输入参数
(gdb)p*rel$5={rd_node={spcNode=1663,dbNode=16402,relNode=50820},rd_smgr=0x2084b00,rd_refcnt=1,rd_backend=-1,rd_islocaltemp=false,rd_isnailed=false,rd_isvalid=true,rd_indexvalid=1'\001',rd_statvalid=false,rd_createSubid=0,rd_newRelfilenodeSubid=0,rd_rel=0x7fad0df139a0,rd_att=0x7fad0df13ab8,rd_id=50820,rd_lockInfo={lockRelId={relId=50820,dbId=16402}},rd_rules=0x0,rd_rulescxt=0x0,trigdesc=0x0,rd_rsdesc=0x0,rd_fkeylist=0x0,rd_fkeyvalid=false,rd_partkeycxt=0x0,rd_partkey=0x0,rd_pdcxt=0x0,rd_partdesc=0x0,rd_partcheck=0x0,rd_indexlist=0x7fad0df12820,rd_oidindex=0,rd_pkindex=0,rd_replidindex=0,rd_statlist=0x0,rd_indexattr=0x0,rd_projindexattr=0x0,rd_keyattr=0x0,rd_pkattr=0x0,rd_idattr=0x0,rd_projidx=0x0,rd_pubactions=0x0,rd_options=0x0,rd_index=0x0,rd_indextuple=0x0,rd_amhandler=0,rd_indexcxt=0x0,rd_amroutine=0x0,rd_opfamily=0x0,rd_opcintype=0x0,rd_support=0x0,rd_supportinfo=0x0,rd_indoption=0x0,rd_indexprs=0x0,rd_indpred=0x0,rd_exclops=0x0,rd_exclprocs=0x0,rd_exclstrats=0x0,rd_amcache=0x0,rd_indcollation=0x0,rd_fdwroutine=0x0,rd_toastoid=0,pgstat_info=0x20785f0}(gdb)
1.初始化相关变量
2.如存在本地map,则首先使用该文件,调用fsm_local_search
3.如果没有本地map也没有FSM,创建本地map,然后调用fsm_local_search
4.使用FSM搜索
4.1获取FSM中原page可用空间对应的catalog —> 0
4.2根据所需空间大小,获取FSM中相应的catalog —> 1
(gdb)n153intsearch_cat=fsm_space_needed_to_cat(spaceNeeded);(gdb)159addr=fsm_get_location(oldPage,&slot);(gdb)pold_cat$1=0(gdb)psearch_cat$2=1(gdb)
4.3根据原页面,获取heap block所在的位置(FSMAddress)
(gdb)n161search_slot=fsm_set_and_search(rel,addr,slot,old_cat,search_cat);(gdb)paddr$3={level=0,logpageno=0}(gdb)
4.4检索获取目标slot
(gdb)n167if(search_slot!=-1)(gdb)psearch_slot$4=4(gdb)
4.5如目标slot合法,则获取相应的block,否则使用fsm_search搜索合适的block
(gdb)n168returnfsm_get_heap_blk(addr,search_slot);(gdb)171}(gdb)RelationGetBufferForTuple(relation=0x7fad0df13788,len=32,otherBuffer=0,options=0,bistate=0x0,vmbuffer=0x7ffe1b797dcc,vmbuffer_other=0x0)athio.c:397397while(targetBlock!=InvalidBlockNumber)(gdb)ptargetBlock$6=4(gdb)
“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。