分析PostgreSQL SetupLockInTable方法中与OOM相关的代码
这篇文章主要介绍“分析PostgreSQL SetupLockInTable方法中与OOM相关的代码”,在日常操作中,相信很多人在分析PostgreSQL SetupLockInTable方法中与OOM相关的代码问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分析PostgreSQL SetupLockInTable方法中与OOM相关的代码”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
有时候我们可能会在PG的日志发现如下信息:
2020-01-0916:29:19.062CST,"pg12","testdb",6193,"[local]",5e16dccd.1831,1,"CREATETABLE",2020-01-0915:57:01CST,2/34,1512004206,ERROR,53200,"outofsharedmemory",,"Youmightneedtoincreasemax_locks_per_transaction.",,,,"CREATETABLEa13030(idint);",,,"psql"2020-01-0916:29:19.379CST,"pg12","testdb",6193,"[local]",5e16dccd.1831,2,"CREATETABLE",2020-01-0915:57:01CST,2/0,1512004206,ERROR,25P02,"currenttransactionisaborted,commandsignoreduntilendoftransactionblock",,,,,,"CREATETABLEa13031(idint);",,,"psql"
直观上来看,OOM似乎与max_locks_per_transaction扯不上什么关系,为什么PG会提示增加max_locks_per_transaction的值呢?
一、源码解读测试脚本
\psetfooteroff\psettuples_only\o/tmp/drop.sqlSELECT'droptableifexiststbl'||id||';'as"--"FROMgenerate_series(1,20000)ASid;\i/tmp/drop.sql\psetfooteroff\psettuples_only\o/tmp/create.sqlSELECT'CREATETABLEtbl'||id||'(idint);'as"--"FROMgenerate_series(1,20000)ASid;\o/tmp/ret.txtbegin;\i/tmp/create.sql
数据结构
HTAB
/**Topcontrolstructureforahashtable---inasharedtable,eachbackend*hasitsowncopy(OKsincenofieldschangeatruntime)*哈希表的顶层控制结构.*在这个共享哈希表中,每一个后台进程都有自己的拷贝*(之所以没有问题是因为fork出来后,在运行期没有字段会变化)*/structHTAB{//指向共享的控制信息HASHHDR*hctl;/*=>sharedcontrolinformation*///段开始目录HASHSEGMENT*dir;/*directoryofsegmentstarts*///哈希函数HashValueFunchash;/*hashfunction*///哈希键比较函数HashCompareFuncmatch;/*keycomparisonfunction*///哈希键拷贝函数HashCopyFunckeycopy;/*keycopyingfunction*///内存分配器HashAllocFuncalloc;/*memoryallocator*///内存上下文MemoryContexthcxt;/*memorycontextifdefaultallocatorused*///表名(用于错误信息)char*tabname;/*tablename(forerrormessages)*///如在共享内存中,则为Tboolisshared;/*trueiftableisinsharedmemory*///如为T,则固定大小不能扩展boolisfixed;/*iftrue,don'tenlarge*//*freezingasharedtableisn'tallowed,sowecankeepstatehere*///不允许冻结共享表,因此这里会保存相关状态boolfrozen;/*true=nomoreinsertsallowed*//*Wekeeplocalcopiesofthesefixedvaluestoreducecontention*///保存这些固定值的本地拷贝,以减少冲突//哈希键长度(以字节为单位)Sizekeysize;/*hashkeylengthinbytes*///段大小,必须为2的幂longssize;/*segmentsize---mustbepowerof2*///段偏移,ssize的对数intsshift;/*segmentshift=log2(ssize)*/};/**Headerstructureforahashtable---containsallchangeableinfo*哈希表的头部结构--存储所有可变信息**Inashared-memoryhashtable,theHASHHDRisinsharedmemory,while*eachbackendhasalocalHTABstruct.Foranon-sharedtable,thereisn't*anyfunctionaldifferencebetweenHASHHDRandHTAB,butweseparatethem*anywaytosharecodebetweensharedandnon-sharedtables.*在共享内存哈希表中,HASHHDR位于共享内存中,每一个后台进程都有一个本地HTAB结构.*对于非共享哈希表,HASHHDR和HTAB没有任何功能性的不同,*但无论如何,我们还是把它们区分为共享和非共享表.*/structHASHHDR{/**Thefreelistcanbecomeapointofcontentioninhigh-concurrencyhash*tables,soweuseanarrayoffreelists,eachwithitsownmutexand*nentriescount,insteadofjustasingleone.Althoughthefreelists*normallyoperateindependently,wewillscavengeentriesfromfreelists*otherthanahashcode'sdefaultfreelistwhennecessary.*在高并发的哈希表中,空闲链表会成为竞争热点,因此我们使用空闲链表数组,*数组中的每一个元素都有自己的mutex和条目统计,而不是使用一个.**Ifthehashtableisnotpartitioned,onlyfreeList[0]isusedandits*spinlockisnotusedatall;callers'lockingisassumedsufficient.*如果哈希表没有分区,那么只有freelist[0]元素是有用的,自旋锁没有任何用处;*调用者锁定被认为已足够OK.*//*Numberoffreeliststobeusedforapartitionedhashtable.*///#defineNUM_FREELISTS32FreeListDatafreeList[NUM_FREELISTS];/*Thesefieldscanchange,butnotinapartitionedtable*///这些域字段可以改变,但不适用于分区表/*Also,dsizecan'tchangeinasharedtable,evenifunpartitioned*///同时,就算是非分区表,共享表的dsize也不能改变//目录大小longdsize;/*directorysize*///已分配的段大小(<=dbsize)longnsegs;/*numberofallocatedsegments(<=dsize)*///正在使用的最大桶IDuint32max_bucket;/*IDofmaximumbucketinuse*///进入整个哈希表的模掩码uint32high_mask;/*masktomodulointoentiretable*///进入低于半个哈希表的模掩码uint32low_mask;/*masktomodulointolowerhalfoftable*//*Thesefieldsarefixedathashtablecreation*///下面这些字段在哈希表创建时已固定//哈希键大小(以字节为单位)Sizekeysize;/*hashkeylengthinbytes*///所有用户元素大小(以字节为单位)Sizeentrysize;/*totaluserelementsizeinbytes*///分区个数(2的幂),或者为0longnum_partitions;/*#partitions(mustbepowerof2),or0*///目标的填充因子longffactor;/*targetfillfactor*///如目录是固定大小,则该值为dsize的上限值longmax_dsize;/*'dsize'limitifdirectoryisfixedsize*///段大小,必须是2的幂longssize;/*segmentsize---mustbepowerof2*///端偏移,ssize的对数intsshift;/*segmentshift=log2(ssize)*///一次性分配的条目个数intnelem_alloc;/*numberofentriestoallocateatonce*/#ifdefHASH_STATISTICS/**Countstatisticshere.NB:statscodedoesn'tbotherwithmutex,so*countscouldbecorruptedabitinapartitionedtable.*统计信息.*注意:统计相关的代码不会影响mutex,因此对于分区表,统计可能有一点点问题*/longaccesses;longcollisions;#endif};/**Per-freelistdata.*空闲链表数据.**Inapartitionedhashtable,eachfreelistisassociatedwithaspecific*setofhashcodes,asdeterminedbytheFREELIST_IDX()macrobelow.*nentriestracksthenumberoflivehashtableentrieshavingthosehashcodes*(NOTthenumberofentriesinthefreelist,asyoumightexpect).*在一个分区哈希表中,每一个空闲链表与特定的hashcodes集合相关,通过下面的FREELIST_IDX()宏进行定义.*nentries跟踪有这些hashcodes的仍存活的hashtable条目个数.*(注意不要搞错,不是空闲的条目个数)**Thecoverageofafreelistmightbemoreorlessthanonepartition,soit*needsitsownlockratherthanrelyingoncallerlocking.Relyingonthat*wouldn'tworkevenifthecoveragewasthesame,becauseoftheoccasional*needto"borrow"entriesfromanotherfreelist;seeget_hash_entry().*空闲链表的覆盖范围可能比一个分区多或少,因此需要自己的锁而不能仅仅依赖调用者的锁.*依赖调用者锁在覆盖面一样的情况下也不会起效,因为偶尔需要从另一个自由列表“借用”条目,详细参见get_hash_entry()**UsinganarrayofFreeListDatainsteadofseparatearraysofmutexes,*nentriesandfreeListshelpstoreducesharingofcachelinesbetween*differentmutexes.*使用FreeListData数组而不是一个独立的mutexes,nentries和freelists数组有助于减少不同mutexes之间的缓存线共享.*/typedefstruct{//该空闲链表的自旋锁slock_tmutex;/*spinlockforthisfreelist*///相关桶中的条目个数longnentries;/*numberofentriesinassociatedbuckets*///空闲元素链HASHELEMENT*freeList;/*chainoffreeelements*/}FreeListData;/**HASHELEMENTistheprivatepartofahashtableentry.Thecaller'sdata*followstheHASHELEMENTstructure(onaMAXALIGN'dboundary).Thehashkey*isexpectedtobeatthestartofthecaller'shashentrydatastructure.*HASHELEMENT是哈希表条目的私有部分.*调用者的数据按照HASHELEMENT结构组织(位于MAXALIGN的边界).*哈希键应位于调用者hash条目数据结构的开始位置.*/typedefstructHASHELEMENT{//链接到相同桶中的下一个条目structHASHELEMENT*link;/*linktonextentryinsamebucket*///该条目的哈希函数结果uint32hashvalue;/*hashfunctionresultforthisentry*/}HASHELEMENT;/*Hashtableheaderstructisanopaquetypeknownonlywithindynahash.c*///哈希表头部结构,非透明类型,用于dynahash.ctypedefstructHASHHDRHASHHDR;/*Hashtablecontrolstructisanopaquetypeknownonlywithindynahash.c*///哈希表控制结构,非透明类型,用于dynahash.ctypedefstructHTABHTAB;/*Parameterdatastructureforhash_create*///hash_create使用的参数数据结构/*Onlythosefieldsindicatedbyhash_flagsneedbeset*///根据hash_flags标记设置相应的字段typedefstructHASHCTL{//分区个数(必须是2的幂)longnum_partitions;/*#partitions(mustbepowerof2)*///段大小longssize;/*segmentsize*///初始化目录大小longdsize;/*(initial)directorysize*///dsize上限longmax_dsize;/*limittodsizeifdirsizeislimited*///填充因子longffactor;/*fillfactor*///哈希键大小(字节为单位)Sizekeysize;/*hashkeylengthinbytes*///参见上述数据结构注释Sizeentrysize;/*totaluserelementsizeinbytes*///HashValueFunchash;/*hashfunction*/HashCompareFuncmatch;/*keycomparisonfunction*/HashCopyFunckeycopy;/*keycopyingfunction*/HashAllocFuncalloc;/*memoryallocator*/MemoryContexthcxt;/*memorycontexttouseforallocations*///共享内存中的哈希头部结构地址HASHHDR*hctl;/*locationofheaderinsharedmem*/}HASHCTL;/*AhashbucketisalinkedlistofHASHELEMENTs*///哈希桶是HASHELEMENTs链表typedefHASHELEMENT*HASHBUCKET;/*Ahashsegmentisanarrayofbucketheaders*///hashsegment是桶数组typedefHASHBUCKET*HASHSEGMENT;/**Hashfunctionsmusthavethissignature.*Hash函数必须有它自己的标识*/typedefuint32(*HashValueFunc)(constvoid*key,Sizekeysize);/**Keycomparisonfunctionsmusthavethissignature.Comparisonfunctions*returnzeroformatch,nonzerofornomatch.(Thecomparisonfunction*definitionisdesignedtoallowmemcmp()andstrncmp()tobeuseddirectly*askeycomparisonfunctions.)*哈希键对比函数必须有自己的标识.*如匹配则对比函数返回0,不匹配返回非0.*(对比函数定义被设计为允许在对比键值时可直接使用memcmp()和strncmp())*/typedefint(*HashCompareFunc)(constvoid*key1,constvoid*key2,Sizekeysize);/**Keycopyingfunctionsmusthavethissignature.Thereturnvalueisnot*used.(Thedefinitionissetuptoallowmemcpy()andstrlcpy()tobe*useddirectly.)*键拷贝函数必须有自己的标识.*返回值无用.*/typedefvoid*(*HashCopyFunc)(void*dest,constvoid*src,Sizekeysize);/**Spaceallocationfunctionforahashtable---designedtomatchmalloc().*Note:thereisnofreefunctionAPI;can'tdestroyahashtableunlessyou*usethedefaultallocator.*哈希表的恐惧分配函数--被设计为与malloc()函数匹配.*注意:这里没有释放函数API;不能销毁哈希表,除非使用默认的分配器.*/typedefvoid*(*HashAllocFunc)(Sizerequest);
get_hash_entry
分配一个新的哈希表条目.如内存溢出则返回NULL.
/**Allocateanewhashtableentryifpossible;returnNULLifoutofmemory.*(Or,iftheunderlyingspaceallocatorthrowserrorforout-of-memory,*wewon'treturnatall.)*如可能,分配一个新的哈希表条目.如内存溢出则返回NULL.*(或者,如果依赖的空间分配器因为内存溢出抛出错误,则不会返回任何信息)*/staticHASHBUCKETget_hash_entry(HTAB*hashp,intfreelist_idx){HASHHDR*hctl=hashp->hctl;HASHBUCKETnewElement;for(;;){//循环/*ifpartitioned,mustlocktotouchnentriesandfreeList*///如为分区哈希表,在访问条目和空闲链表时,必须锁定if(IS_PARTITIONED(hctl))SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);/*trytogetanentryfromthefreelist*///从空闲链表中尝试获取一个条目newElement=hctl->freeList[freelist_idx].freeList;if(newElement!=NULL)break;if(IS_PARTITIONED(hctl))SpinLockRelease(&hctl->freeList[freelist_idx].mutex);/**Nofreeelementsinthisfreelist.Inapartitionedtable,there*mightbeentriesinotherfreelists,buttoreducecontentionwe*prefertofirsttrytogetanotherchunkofbucketsfromthemain*shmemallocator.Ifthatfails,though,we*MUST*rootthroughall*theotherfreelistsbeforegivingup.Therearemultiplecallers*thatassumethattheycanallocateeveryelementintheinitially*requestedtablesize,orthatdeletinganelementguaranteesthey*caninsertanewelement,evenifsharedmemoryisentirelyfull.*Failingbecausetheneededelementisinadifferentfreelistis*notacceptable.*在空闲链表中没有空闲条目.在分区哈希表中,在其他空闲链表中可能存在条目,*但为了减少争用,我们期望首先尝试从主shmem分配器中获取桶中的其他chunk.*如果失败,我们必须在放弃之前从根节点开始遍历所有其他空闲链表.*存在多个调用者假定它们可以在初始的请求哈希表大小内分配每一个元素,*或者甚至在共享内存全满的情况下删除元素可以保证它们可以插入一个新元素.*之所以失败是因为所需要的元素在不同的空闲链表中是不可接受的.*/if(!element_alloc(hashp,hctl->nelem_alloc,freelist_idx)){//本空闲链表不能分配内存intborrow_from_idx;if(!IS_PARTITIONED(hctl))//非分区哈希表,返回NULL,意味着内存溢出了.returnNULL;/*outofmemory*//*trytoborrowelementfromanotherfreelist*///尝试从其他空闲链表浏览元素borrow_from_idx=freelist_idx;for(;;){//-------开始遍历其他空闲链表borrow_from_idx=(borrow_from_idx+1)%NUM_FREELISTS;if(borrow_from_idx==freelist_idx)//已经完成整个空闲链表的遍历,退出break;/*examinedallfreelists,fail*///获取自旋锁SpinLockAcquire(&(hctl->freeList[borrow_from_idx].mutex));newElement=hctl->freeList[borrow_from_idx].freeList;if(newElement!=NULL){hctl->freeList[borrow_from_idx].freeList=newElement->link;SpinLockRelease(&(hctl->freeList[borrow_from_idx].mutex));/*careful:countthenewelementinitsproperfreelist*///小心:在合适的空闲链表上统计新的元素SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);hctl->freeList[freelist_idx].nentries++;SpinLockRelease(&hctl->freeList[freelist_idx].mutex);returnnewElement;}SpinLockRelease(&(hctl->freeList[borrow_from_idx].mutex));}/*noelementsavailabletoborroweither,sooutofmemory*///已无可用空间,内存溢出returnNULL;}}/*removeentryfromfreelist,bumpnentries*///从空闲链表中移除条目,nentries+1hctl->freeList[freelist_idx].freeList=newElement->link;hctl->freeList[freelist_idx].nentries++;if(IS_PARTITIONED(hctl))SpinLockRelease(&hctl->freeList[freelist_idx].mutex);returnnewElement;}二、跟踪分析
跟踪SetupLockInTable,进入hash_search_with_hash_value
(gdb)bSetupLockInTableiflockmode==8Breakpoint2at0x8ccf4e:filelock.c,line1131.(gdb)cContinuing.Breakpoint2,SetupLockInTable(lockMethodTable=0xc8dba0<default_lockmethod>,proc=0x7f293e800b70,locktag=0x7fff167a9250,hashcode=1823181291,lockmode=8)atlock.c:11311131lock=(LOCK*)hash_search_with_hash_value(LockMethodLockHash,(gdb)stephash_search_with_hash_value(hashp=0x1210160,keyPtr=0x7fff167a9250,hashvalue=1823181291,action=HASH_ENTER_NULL,foundPtr=0x7fff167a90bf)atdynahash.c:925925HASHHDR*hctl=hashp->hctl;
查看HTAB(hashp)和HASHHDR(hctl)
(gdb)n926intfreelist_idx=FREELIST_IDX(hctl,hashvalue);(gdb)p*hashp$8={hctl=0x7f293e443980,dir=0x7f293e443cd8,hash=0xa79ac6<tag_hash>,match=0x47cb70<memcmp@plt>,keycopy=0x47d0a0<memcpy@plt>,alloc=0x8c3419<ShmemAllocNoError>,hcxt=0x0,tabname=0x12101c0"LOCKhash",isshared=true,isfixed=false,frozen=false,keysize=16,ssize=256,sshift=8}(gdb)p*hctl$9={freeList={{mutex=0'\000',nentries=389,freeList=0x0},{mutex=0'\000',nentries=392,freeList=0x0},{mutex=0'\000',nentries=400,freeList=0x0},{mutex=0'\000',nentries=382,freeList=0x7f293ebd1b00},{mutex=0'\000',nentries=439,freeList=0x7f293ebc89a0},{mutex=0'\000',nentries=391,freeList=0x7f293ebc1b20},{mutex=0'\000',nentries=411,freeList=0x7f293eb91680},{mutex=0'\000',nentries=395,freeList=0x0},{mutex=0'\000',nentries=402,freeList=0x0},{mutex=0'\000',nentries=416,freeList=0x7f293ebcb338},{mutex=0'\000',nentries=431,freeList=0x7f293ebc3170},{mutex=0'\000',nentries=421,freeList=0x0},{mutex=0'\000',nentries=406,freeList=0x7f293eb995a8},{mutex=0'\000',nentries=409,freeList=0x7f293ebd2238},{mutex=0'\000',nentries=411,freeList=0x7f293ebd1f98},{mutex=0'\000',nentries=386,freeList=0x0},{mutex=0'\000',nentries=412,freeList=0x0},{mutex=0'\000',nentries=424,freeList=0x0},{mutex=0'\000',nentries=394,freeList=0x7f293ebd1da0},{mutex=0'\000',nentries=401,freeList=0x0},{mutex=0'\000',nentries=408,freeList=0x7f293eb99500},{mutex=0'\000',nentries=437,freeList=0x0},{mutex=0'\000',nentries=429,freeList=0x7f293ebd2778},{mutex=0'\000',nentries=386,freeList=0x0},{mutex=0'\000',nentries=410,freeList=0x7f293ebd2d60},{mutex=0'\000',nentries=416,freeList=0x0},{mutex=0'\000',nentries=412,freeList=0x7f293ebd1c50},{mutex=0'\000',nentries=436,freeList=0x7f293ebd1ba8},{mutex=0'\000',nentries=380,freeList=0x7f293ebd1cf8},{mutex=0'\000',nentries=428,freeList=0x0},{mutex=0'\000',nentries=405,freeList=0x0},{mutex=0'\000',nentries=372,freeList=0x0}},dsize=256,nsegs=16,max_bucket=4095,high_mask=8191,low_mask=4095,keysize=16,entrysize=152,num_partitions=16,ffactor=1,max_dsize=256,ssize=256,sshift=8,nelem_alloc=48}
可以看到,hctl中的freelist数组,数组中每个元素的freeList,如不为0x0(NULL),则说明还有空闲空间,否则说明已无空间.
(gdb)n949if(action==HASH_ENTER||action==HASH_ENTER_NULL)(gdb)pfreelist_idx$10=11(gdb)phctl->freeList[11]$11={mutex=0'\000',nentries=421,freeList=0x0}(gdb)n956if(!IS_PARTITIONED(hctl)&&!hashp->frozen&&(gdb)965bucket=calc_bucket(hctl,hashvalue);-->hash桶(gdb)967segment_num=bucket>>hashp->sshift;-->根据hash桶获取段(gdb)968segment_ndx=MOD(bucket,hashp->ssize);-->根据hash桶获取段内偏移(gdb)970segp=hashp->dir[segment_num];-->获取hash段指针(gdb)972if(segp==NULL)(gdb)pbucket$12=2539(gdb)psegment_num$13=9(gdb)psegment_ndx$14=235(gdb)p*segp$15=(HASHBUCKET)0x7f293e44de98(gdb)p**segp$16={link=0x0,hashvalue=3817199872}(gdb)n975prevBucketPtr=&segp[segment_ndx];-->HASHBUCKET指针的指针(gdb)976currBucket=*prevBucketPtr;-->HASHBUCKET指针(gdb)981match=hashp->match;/*saveonefetchininnerloop*/(gdb)982keysize=hashp->keysize;/*ditto*/(gdb)pmatch$17=(HashCompareFunc)0x47cb70<memcmp@plt>-->hash函数(gdb)n984while(currBucket!=NULL)-->沿着碰撞链循环获取hash桶(gdb)986if(currBucket->hashvalue==hashvalue&&-->退出条件:找到匹配的元素(gdb)989prevBucketPtr=&(currBucket->link);(gdb)990currBucket=*prevBucketPtr;(gdb)984while(currBucket!=NULL)-->退出条件:hash桶指针为NULL,没有找到元素(gdb)997if(foundPtr)(gdb)pfoundPtr$18=(_Bool*)0x7fff167a90bf(gdb)n998*foundPtr=(bool)(currBucket!=NULL);(gdb)1003switch(action)(gdb)1042Assert(hashp->alloc!=DynaHashAlloc);(gdb)1047if(currBucket!=NULL)(gdb)1051if(hashp->frozen)(gdb)1055currBucket=get_hash_entry(hashp,freelist_idx);(gdb)stepget_hash_entry(hashp=0x1210160,freelist_idx=11)atdynahash.c:12521252HASHHDR*hctl=hashp->hctl;(gdb)n注:#defineIS_PARTITIONED(hctl)((hctl)->num_partitions!=0)1258if(IS_PARTITIONED(hctl))(gdb)p*hctl$19={freeList={{mutex=0'\000',nentries=389,freeList=0x0},{mutex=0'\000',nentries=392,freeList=0x0},{mutex=0'\000',nentries=400,freeList=0x0},{mutex=0'\000',nentries=382,freeList=0x7f293ebd1b00},{mutex=0'\000',nentries=439,freeList=0x7f293ebc89a0},{mutex=0'\000',nentries=391,freeList=0x7f293ebc1b20},{mutex=0'\000',nentries=411,freeList=0x7f293eb91680},{mutex=0'\000',nentries=395,freeList=0x0},{mutex=0'\000',nentries=402,freeList=0x0},{mutex=0'\000',nentries=416,freeList=0x7f293ebcb338},{mutex=0'\000',nentries=431,freeList=0x7f293ebc3170},{mutex=0'\000',nentries=421,freeList=0x0},{mutex=0'\000',nentries=406,freeList=0x7f293eb995a8},{mutex=0'\000',nentries=409,freeList=0x7f293ebd2238},{mutex=0'\000',nentries=411,freeList=0x7f293ebd1f98},{mutex=0'\000',nentries=386,freeList=0x0},{mutex=0'\000',nentries=412,freeList=0x0},{mutex=0'\000',nentries=424,freeList=0x0},{mutex=0'\000',nentries=394,freeList=0x7f293ebd1da0},{mutex=0'\000',nentries=401,freeList=0x0},{mutex=0'\000',nentries=408,freeList=0x7f293eb99500},{mutex=0'\000',nentries=437,freeList=0x0},{mutex=0'\000',nentries=429,freeList=0x7f293ebd2778},{mutex=0'\000',nentries=386,freeList=0x0},{mutex=0'\000',nentries=410,freeList=0x7f293ebd2d60},{mutex=0'\000',nentries=416,freeList=0x0},{mutex=0'\000',nentries=412,freeList=0x7f293ebd1c50},{mutex=0'\000',nentries=436,freeList=0x7f293ebd1ba8},{mutex=0'\000',nentries=380,freeList=0x7f293ebd1cf8},{mutex=0'\000',nentries=428,freeList=0x0},{mutex=0'\000',nentries=405,freeList=0x0},{mutex=0'\000',nentries=372,freeList=0x0}},dsize=256,nsegs=16,max_bucket=4095,high_mask=8191,low_mask=4095,keysize=16,entrysize=152,num_partitions=16,ffactor=1,max_dsize=256,ssize=256,sshift=8,nelem_alloc=48}(gdb)n1259SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);(gdb)1262newElement=hctl->freeList[freelist_idx].freeList;-->尝试从空闲链表中获取新元素(gdb)1264if(newElement!=NULL)-->不为NULL,则返回,否则,尝试扩展(gdb)pnewElement$20=(HASHBUCKET)0x0(gdb)n1267if(IS_PARTITIONED(hctl))(gdb)1268SpinLockRelease(&hctl->freeList[freelist_idx].mutex);(gdb)1282if(!element_alloc(hashp,hctl->nelem_alloc,freelist_idx))-->扩展(gdb)stepelement_alloc(hashp=0x1210160,nelem=48,freelist_idx=11)atdynahash.c:1659-->进入element_alloc1659HASHHDR*hctl=hashp->hctl;(gdb)n1666if(hashp->isfixed)(gdb)1670elementSize=MAXALIGN(sizeof(HASHELEMENT))+MAXALIGN(hctl->entrysize);-->确保地址对齐(gdb)1672CurrentDynaHashCxt=hashp->hcxt;(gdb)pelementSize$21=168(gdb)n1673firstElement=(HASHELEMENT*)hashp->alloc(nelem*elementSize);-->分配空间(gdb)1675if(!firstElement)(gdb)pfirstElement$22=(HASHELEMENT*)0x0(gdb)pnelem*elementSize$23=8064(gdb)n1676returnfalse;-->扩展失败,返回false(gdb)1700}(gdb)get_hash_entry(hashp=0x1210160,freelist_idx=11)atdynahash.c:12861286if(!IS_PARTITIONED(hctl))(gdb)1290borrow_from_idx=freelist_idx;-->该空闲链表无法扩展,寻找下一个空闲链表(gdb)1293borrow_from_idx=(borrow_from_idx+1)%NUM_FREELISTS;-->简单的+1后取模(gdb)pfreelist_idx$24=11(gdb)n1294if(borrow_from_idx==freelist_idx)-->找了一圈,回到原点,内存不足,这时候会报错(gdb)pNUM_FREELISTS$25=32(gdb)pborrow_from_idx$26=12(gdb)n1297SpinLockAcquire(&(hctl->freeList[borrow_from_idx].mutex));(gdb)1298newElement=hctl->freeList[borrow_from_idx].freeList;(gdb)1300if(newElement!=NULL)(gdb)1302hctl->freeList[borrow_from_idx].freeList=newElement->link;(gdb)1303SpinLockRelease(&(hctl->freeList[borrow_from_idx].mutex));(gdb)1306SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);(gdb)1307hctl->freeList[freelist_idx].nentries++;(gdb)1308SpinLockRelease(&hctl->freeList[freelist_idx].mutex);(gdb)pnewElement$27=(HASHBUCKET)0x7f293eb995a8(gdb)n1310returnnewElement;-->找到了空闲元素,返回(gdb)1329}(gdb)hash_search_with_hash_value(hashp=0x1210160,keyPtr=0x7fff167a9250,hashvalue=1823181291,action=HASH_ENTER_NULL,foundPtr=0x7fff167a90bf)atdynahash.c:10561056if(currBucket==NULL)(gdb)1073*prevBucketPtr=currBucket;(gdb)1074currBucket->link=NULL;(gdb)1077currBucket->hashvalue=hashvalue;(gdb)1078hashp->keycopy(ELEMENTKEY(currBucket),keyPtr,keysize);(gdb)1087return(void*)ELEMENTKEY(currBucket);(gdb)1093}(gdb)SetupLockInTable(lockMethodTable=0xc8dba0<default_lockmethod>,proc=0x7f293e800b70,locktag=0x7fff167a9250,hashcode=1823181291,lockmode=8)atlock.c:11361136if(!lock)(gdb)1142if(!found)(gdb)1144lock->grantMask=0;(gdb)1145lock->waitMask=0;(gdb)1146SHMQueueInit(&(lock->procLocks));(gdb)1147ProcQueueInit(&(lock->waitProcs));(gdb)1148lock->nRequested=0;(gdb)1149lock->nGranted=0;(gdb)1150MemSet(lock->requested,0,sizeof(int)*MAX_LOCKMODES);(gdb)1151MemSet(lock->granted,0,sizeof(int)*MAX_LOCKMODES);(gdb)1165proclocktag.myLock=lock;(gdb)1166proclocktag.myProc=proc;(gdb)1168proclock_hashcode=ProcLockHashCode(&proclocktag,hashcode);(gdb)1173proclock=(PROCLOCK*)hash_search_with_hash_value(LockMethodProcLockHash,(gdb)1178if(!proclock)(gdb)pproclock$28=(PROCLOCK*)0x7f293ebc6ec0(gdb)n1203if(!found)(gdb)1205uint32partition=LockHashPartition(hashcode);(gdb)1217proclock->groupLeader=proc->lockGroupLeader!=NULL?(gdb)1218proc->lockGroupLeader:proc;(gdb)1217proclock->groupLeader=proc->lockGroupLeader!=NULL?(gdb)1219proclock->holdMask=0;(gdb)1220proclock->releaseMask=0;(gdb)1222SHMQueueInsertBefore(&lock->procLocks,&proclock->lockLink);(gdb)1223SHMQueueInsertBefore(&(proc->myProcLocks[partition]),(gdb)1275lock->nRequested++;(gdb)1276lock->requested[lockmode]++;(gdb)1277Assert((lock->nRequested>0)&&(lock->requested[lockmode]>0));(gdb)1283if(proclock->holdMask&LOCKBIT_ON(lockmode))(gdb)1289returnproclock;(gdb)1290}(gdb)LockAcquireExtended(locktag=0x7fff167a9250,lockmode=8,sessionLock=false,dontWait=false,reportMemoryError=true,locallockp=0x7fff167a9248)atlock.c:956956if(!proclock)(gdb)cContinuing.
到此,关于“分析PostgreSQL SetupLockInTable方法中与OOM相关的代码”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。