PostgreSQL的vacuum过程中heap_vacuum_rel函数分析
这篇文章主要介绍“PostgreSQL的vacuum过程中heap_vacuum_rel函数分析”,在日常操作中,相信很多人在PostgreSQL的vacuum过程中heap_vacuum_rel函数分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL的vacuum过程中heap_vacuum_rel函数分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
本节简单介绍了PostgreSQL手工执行vacuum的实现逻辑,主要分析了ExecVacuum->vacuum->vacuum_rel->heap_vacuum_rel函数的实现逻辑。
一、数据结构宏定义
Vacuum和Analyze命令选项
/*----------------------*VacuumandAnalyzeStatements*Vacuum和Analyze命令选项**Eventhoughthesearenominallytwostatements,it'sconvenienttouse*justonenodetypeforboth.NotethatatleastoneofVACOPT_VACUUM*andVACOPT_ANALYZEmustbesetinoptions.*虽然在这里有两种不同的语句,但只需要使用统一的Node类型即可.*注意至少VACOPT_VACUUM/VACOPT_ANALYZE在选项中设置.*----------------------*/typedefenumVacuumOption{VACOPT_VACUUM=1<<0,/*doVACUUM*/VACOPT_ANALYZE=1<<1,/*doANALYZE*/VACOPT_VERBOSE=1<<2,/*printprogressinfo*/VACOPT_FREEZE=1<<3,/*FREEZEoption*/VACOPT_FULL=1<<4,/*FULL(non-concurrent)vacuum*/VACOPT_SKIP_LOCKED=1<<5,/*skipifcannotgetlock*/VACOPT_SKIPTOAST=1<<6,/*don'tprocesstheTOASTtable,ifany*/VACOPT_DISABLE_PAGE_SKIPPING=1<<7/*don'tskipanypages*/}VacuumOption;
VacuumStmt
存储vacuum命令的option&Relation链表
typedefstructVacuumStmt{NodeTagtype;//Tag//VacuumOption位标记intoptions;/*ORofVacuumOptionflags*///VacuumRelation链表,如为NIL-->所有Relation.List*rels;/*listofVacuumRelation,orNILforall*/}VacuumStmt;
VacuumParams
vacuum命令参数
/**ParameterscustomizingbehaviorofVACUUMandANALYZE.*客户端调用VACUUM/ANALYZE时的定制化参数*/typedefstructVacuumParams{//最小freezeage,-1表示使用默认intfreeze_min_age;/*minfreezeage,-1tousedefault*///扫描整个table的freezeageintfreeze_table_age;/*ageatwhichtoscanwholetable*///最小的multixactfreezeage,-1表示默认intmultixact_freeze_min_age;/*minmultixactfreezeage,-1to*usedefault*///扫描全表的freezeage,-1表示默认intmultixact_freeze_table_age;/*multixactageatwhichtoscan*wholetable*///是否强制wraparound?boolis_wraparound;/*forceafor-wraparoundvacuum*///以毫秒为单位的最小执行阈值intlog_min_duration;/*minimumexecutionthresholdinmsat*whichverboselogsareactivated,-1*tousedefault*/}VacuumParams;
VacuumRelation
VACUUM/ANALYZE命令的目标表信息
/**InfoaboutasingletargettableofVACUUM/ANALYZE.*VACUUM/ANALYZE命令的目标表信息.**IftheOIDfieldisset,italwaysidentifiesthetabletoprocess.*ThentherelationfieldcanbeNULL;ifitisn't,it'susedonlytoreport*failuretoopen/locktherelation.*如设置了OID字段,该值通常是将要处理的数据表.*那么关系字段可以为空;如果不是,则仅用于报告未能打开/锁定关系。*/typedefstructVacuumRelation{NodeTagtype;RangeVar*relation;/*tablenametoprocess,orNULL*/Oidoid;/*table'sOID;InvalidOidifnotlookedup*/List*va_cols;/*listofcolumnnames,orNILforall*/}VacuumRelation;
BufferAccessStrategy
Buffer访问策略对象
/**Bufferidentifiers.*Buffer标识符**Zeroisinvalid,positiveistheindexofasharedbuffer(1..NBuffers),*negativeistheindexofalocalbuffer(-1..-NLocBuffer).*0表示无效,正整数表示共享buffer的索引(1..N),*负数是本地buffer的索引(-1..-N)*/typedefintBuffer;#defineInvalidBuffer0/**Bufferaccessstrategyobjects.*Buffer访问策略对象**BufferAccessStrategyDataisprivatetofreelist.c*BufferAccessStrategyData对freelist.c来说是私有的*/typedefstructBufferAccessStrategyData*BufferAccessStrategy;/**Private(non-shared)stateformanagingaringofsharedbufferstore-use.*ThisiscurrentlytheonlykindofBufferAccessStrategyobject,butsomeday*wemighthavemorekinds.*私有状态,用于管理可重用的环形缓冲区.*目前只有这么一种缓冲区访问策略对象,但未来某一天可以拥有更多.*/typedefstructBufferAccessStrategyData{/*Overallstrategytype*///全局的策略类型BufferAccessStrategyTypebtype;/*Numberofelementsinbuffers[]array*///buffers[]中的元素个数intring_size;/**Indexofthe"current"slotinthering,ie,theonemostrecently*returnedbyGetBufferFromRing.*环形缓冲区中当前slot的索引,最近访问的通过函数GetBufferFromRing返回.*/intcurrent;/**TrueifthebufferjustreturnedbyStrategyGetBufferhadbeeninthe*ringalready.*如正好通过StrategyGetBuffer返回的buffer已在环形缓冲区中,则返回T*/boolcurrent_was_in_ring;/**Arrayofbuffernumbers.InvalidBuffer(thatis,zero)indicateswe*havenotyetselectedabufferforthisringslot.Forallocation*simplicitythisispalloc'dtogetherwiththefixedfieldsofthe*struct.*buffer编号数组.*InvalidBuffer(即:0)表示我们还没有为该slot选择buffer.*为了分配的简单性,这是palloc'd与结构的固定字段。*/Bufferbuffers[FLEXIBLE_ARRAY_MEMBER];}BufferAccessStrategyData;//Block结构体指针typedefvoid*Block;/*PossibleargumentsforGetAccessStrategy()*///GetAccessStrategy()函数可取值的参数typedefenumBufferAccessStrategyType{//常规的随机访问BAS_NORMAL,/*Normalrandomaccess*///大规模的只读扫描BAS_BULKREAD,/*Largeread-onlyscan(hintbitupdatesare*ok)*///大量的多块写(如COPYIN)BAS_BULKWRITE,/*Largemulti-blockwrite(e.g.COPYIN)*///VACUUMBAS_VACUUM/*VACUUM*/}BufferAccessStrategyType;
LVRelStats
typedefstructLVRelStats{/*hasindex=truemeanstwo-passstrategy;falsemeansone-pass*///T表示two-passstrategy,F表示one-passstrategyboolhasindex;/*Overallstatisticsaboutrel*///rel的全局统计信息//pg_class.relpages的上一个值BlockNumberold_rel_pages;/*previousvalueofpg_class.relpages*///pages的总数BlockNumberrel_pages;/*totalnumberofpages*///扫描的pagesBlockNumberscanned_pages;/*numberofpagesweexamined*///由于pin跳过的pagesBlockNumberpinskipped_pages;/*#ofpagesweskippedduetoapin*///跳过的frozenpagesBlockNumberfrozenskipped_pages;/*#offrozenpagesweskipped*///计算其元组的pagesBlockNumbertupcount_pages;/*pageswhosetupleswecounted*///pg_class.reltuples的前值doubleold_live_tuples;/*previousvalueofpg_class.reltuples*///新估算的总元组数doublenew_rel_tuples;/*newestimatedtotal#oftuples*///新估算的存活元组数doublenew_live_tuples;/*newestimatedtotal#oflivetuples*///新估算的废弃元组数doublenew_dead_tuples;/*newestimatedtotal#ofdeadtuples*///已清除的pagesBlockNumberpages_removed;//已删除的tuplesdoubletuples_deleted;//实际上是非空page+1BlockNumbernonempty_pages;/*actually,lastnonemptypage+1*//*ListofTIDsoftuplesweintendtodelete*//*NB:thislistisorderedbyTIDaddress*///将要删除的元组TIDs链表//注意:该链表已使用TID地址排序//当前的入口/条目数intnum_dead_tuples;/*current#ofentries*///数组中已分配的slots(最大已废弃元组数)intmax_dead_tuples;/*#slotsallocatedinarray*///ItemPointerData数组ItemPointerdead_tuples;/*arrayofItemPointerData*///扫描的索引数intnum_index_scans;//最后被清除的事务IDTransactionIdlatestRemovedXid;//是否存在waiter?boollock_waiter_detected;}LVRelStats;
PGRUsage
pg_rusage_init/pg_rusage_show的状态结构体
/*Statestructureforpg_rusage_init/pg_rusage_show*///pg_rusage_init/pg_rusage_show的状态结构体typedefstructPGRUsage{structtimevaltv;structrusageru;}PGRUsage;structrusage{structtimevalru_utime;/*usertimeused*/structtimevalru_stime;/*systemtimeused*/};structtimeval{__time_ttv_sec;/*秒数.Seconds.*/__suseconds_ttv_usec;/*微秒数.Microseconds-->这个英文注释有问题.*/};二、源码解读
heap_vacuum_rel() — 为heap relation执行VACUUM
大体逻辑如下:
1.初始化相关变量,如本地变量/日志记录级别/访问策略等
2.调用vacuum_set_xid_limits计算最老的xmin和冻结截止点
3.判断是否执行全表(不跳过pages)扫描,标记变量为aggressive
4.初始化统计信息结构体vacrelstats
5.打开索引,执行函数lazy_scan_heap进行vacuuming,关闭索引
6.更新pg_class中的统计信息
7.收尾工作
/**heap_vacuum_rel()--performVACUUMforoneheaprelation*heap_vacuum_rel()--为heaprelation执行VACUUM**Thisroutinevacuumsasingleheap,cleansoutitsindexes,and*updatesitsrelpagesandreltuplesstatistics.*该处理过程vacuum一个单独的heap,清除索引并更新relpages和reltuples统计信息.**Atentry,wehavealreadyestablishedatransactionandopened*andlockedtherelation.*在该调用入口,我们已经给创建了事务并且已经打开&锁定了relation.*/voidheap_vacuum_rel(Relationonerel,intoptions,VacuumParams*params,BufferAccessStrategybstrategy){LVRelStats*vacrelstats;//统计信息Relation*Irel;//关系指针intnindexes;PGRUsageru0;//状态结构体TimestampTzstarttime=0;//时间戳longsecs;//秒数intusecs;//微秒数doubleread_rate,//读比率write_rate;//写比率//是否扫描所有未冻结的pages?boolaggressive;/*shouldwescanallunfrozenpages?*///实际上是否扫描了所有这样的pages?boolscanned_all_unfrozen;/*actuallyscannedallsuchpages?*/TransactionIdxidFullScanLimit;MultiXactIdmxactFullScanLimit;BlockNumbernew_rel_pages;BlockNumbernew_rel_allvisible;doublenew_live_tuples;TransactionIdnew_frozen_xid;MultiXactIdnew_min_multi;Assert(params!=NULL);/*measureelapsedtimeiffautovacuumloggingrequiresit*///如autovacuum日志记录需要,则测量耗费的时间if(IsAutoVacuumWorkerProcess()&¶ms->log_min_duration>=0){pg_rusage_init(&ru0);starttime=GetCurrentTimestamp();}if(options&VACOPT_VERBOSE)//需要VERBOSEelevel=INFO;elseelevel=DEBUG2;pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,RelationGetRelid(onerel));vac_strategy=bstrategy;//计算最老的xmin和冻结截止点//输出:OldestXmin/FreezeLimit/FreezeLimit/MultiXactCutoff/mxactFullScanLimitvacuum_set_xid_limits(onerel,params->freeze_min_age,params->freeze_table_age,params->multixact_freeze_min_age,params->multixact_freeze_table_age,&OldestXmin,&FreezeLimit,&xidFullScanLimit,&MultiXactCutoff,&mxactFullScanLimit);/**Werequestanaggressivescanifthetable'sfrozenXidisnowolder*thanorequaltotherequestedXidfull-tablescanlimit;orifthe*table'sminimumMultiXactIdisolderthanorequaltotherequested*mxidfull-tablescanlimit;orifDISABLE_PAGE_SKIPPINGwasspecified.*如果表的frozenXid现在大于或等于请求的Xid全表扫描限制,则请求进行主动扫描;*或者如果表的最小MultiXactId大于或等于请求的mxid全表扫描限制;*或者,如果指定了disable_page_skip。*///比较onerel->rd_rel->relfrozenxid&xidFullScanLimit//如小于等于,则aggressive为T,否则为Faggressive=TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,xidFullScanLimit);//比较onerel->rd_rel->relminmxid&mxactFullScanLimit//如小于等于,则aggressive为T//否则aggressive原值为T,则为T,否则为Faggressive|=MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,mxactFullScanLimit);if(options&VACOPT_DISABLE_PAGE_SKIPPING)//禁用跳过页,则强制为Taggressive=true;//分配统计结构体内存vacrelstats=(LVRelStats*)palloc0(sizeof(LVRelStats));//记录统计信息vacrelstats->old_rel_pages=onerel->rd_rel->relpages;vacrelstats->old_live_tuples=onerel->rd_rel->reltuples;vacrelstats->num_index_scans=0;vacrelstats->pages_removed=0;vacrelstats->lock_waiter_detected=false;/*Openallindexesoftherelation*///打开该relation所有的索引vac_open_indexes(onerel,RowExclusiveLock,&nindexes,&Irel);vacrelstats->hasindex=(nindexes>0);/*Dothevacuuming*///执行vacuuminglazy_scan_heap(onerel,options,vacrelstats,Irel,nindexes,aggressive);/*Donewithindexes*///已完成index的处理vac_close_indexes(nindexes,Irel,NoLock);/**Computewhetherweactuallyscannedtheallunfrozenpages.Ifwedid,*wecanadjustrelfrozenxidandrelminmxid.*计算我们实际上是否扫描了所有unfrozenpages.*如果扫描了,则需要调整relfrozenxid和relminmxid.**NB:Weneedtocheckthisbeforetruncatingtherelation,becausethat*willchange->rel_pages.*注意:我们需要在截断relation前执行检查,因为这会改变rel_pages.*/if((vacrelstats->scanned_pages+vacrelstats->frozenskipped_pages)<vacrelstats->rel_pages){Assert(!aggressive);scanned_all_unfrozen=false;}else//扫描pages+冻结跳过的pages>=总pages,则为Tscanned_all_unfrozen=true;/**Optionallytruncatetherelation.*可选的,截断relation*/if(should_attempt_truncation(vacrelstats))lazy_truncate_heap(onerel,vacrelstats);/*Reportthatwearenowdoingfinalcleanup*///通知其他进程,正在进行最后的清理pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,PROGRESS_VACUUM_PHASE_FINAL_CLEANUP);/**Updatestatisticsinpg_class.*更新pg_class中的统计信息.**Acornercasehereisthatifwescannednopagesatallbecauseevery*pageisall-visible,weshouldnotupdaterelpages/reltuples,because*wehavenonewinformationtocontribute.Inparticularthiskeepsus*fromreplacingrelpages=reltuples=0(whichmeans"unknowntuple*density")withnonzerorelpagesandreltuples=0(whichmeans"zero*tupledensity")unlessthere'ssomeactualevidenceforthelatter.*这里的一个极端情况是,如果每个页面都是可见的,这时候根本没有扫描任何页面,*那么就不应该更新relpages/reltuples,因为我们没有新信息可以更新。*特别地,这阻止我们将relpages=reltuple=0(这意味着“未知的元组密度”)替换*为非零relpages和reltuple=0(这意味着“零元组密度”),*除非有关于后者的一些实际的证据。**It'simportantthatweusetupcount_pagesandnotscanned_pagesforthe*checkdescribedabove;scanned_pagescountspageswherewecouldnot*getcleanuplock,andwhichwereprocessedonlyforfrozenxidpurposes.*对于上面描述的检查,使用tupcount_pages而不是scanned_pages是很重要的;*scanned_pages对无法获得清理锁的页面进行计数,这些页面仅用于frozenxid目的。**Wedoupdaterelallvisibleeveninthecornercase,sinceifthetable*isall-visiblewe'ddefinitelyliketoknowthat.Butclampthevalue*tobenotmorethanwhatwe'resettingrelpagesto.*即使在极端情况下,我们也会更新relallvisible,因为如果表是all-visible的,那我们肯定想知道这个.*但是不要超过我们设置relpages的值。**Also,don'tchangerelfrozenxid/relminmxidifweskippedanypages,*sincethenwedon'tknowforcertainthatalltupleshaveanewerxmin.*同时,如果我们跳过了所有的页面,不能更新relfrozenxid/relminmxid,*因为从那时起,我们不能确定所有元组是否都有更新的xmin.*/new_rel_pages=vacrelstats->rel_pages;new_live_tuples=vacrelstats->new_live_tuples;if(vacrelstats->tupcount_pages==0&&new_rel_pages>0){//实际处理的tuple为0而且总页面不为0,则调整回原页数new_rel_pages=vacrelstats->old_rel_pages;new_live_tuples=vacrelstats->old_live_tuples;}visibilitymap_count(onerel,&new_rel_allvisible,NULL);if(new_rel_allvisible>new_rel_pages)new_rel_allvisible=new_rel_pages;new_frozen_xid=scanned_all_unfrozen?FreezeLimit:InvalidTransactionId;new_min_multi=scanned_all_unfrozen?MultiXactCutoff:InvalidMultiXactId;//更新pg_class中的统计信息vac_update_relstats(onerel,new_rel_pages,new_live_tuples,new_rel_allvisible,vacrelstats->hasindex,new_frozen_xid,new_min_multi,false);/*reportresultstothestatscollector,too*///同时,发送结果给统计收集器pgstat_report_vacuum(RelationGetRelid(onerel),onerel->rd_rel->relisshared,new_live_tuples,vacrelstats->new_dead_tuples);pgstat_progress_end_command();/*andlogtheactionifappropriate*///并在适当的情况下记录操作if(IsAutoVacuumWorkerProcess()&¶ms->log_min_duration>=0){//autovacuum&&参数log_min_duration>=0TimestampTzendtime=GetCurrentTimestamp();if(params->log_min_duration==0||TimestampDifferenceExceeds(starttime,endtime,params->log_min_duration)){StringInfoDatabuf;char*msgfmt;TimestampDifference(starttime,endtime,&secs,&usecs);read_rate=0;write_rate=0;if((secs>0)||(usecs>0)){read_rate=(double)BLCKSZ*VacuumPageMiss/(1024*1024)/(secs+usecs/1000000.0);write_rate=(double)BLCKSZ*VacuumPageDirty/(1024*1024)/(secs+usecs/1000000.0);}/**Thisisprettymessy,butwesplititupsothatwecanskip*emittingindividualpartsofthemessagewhennotapplicable.*/initStringInfo(&buf);if(params->is_wraparound){if(aggressive)msgfmt=_("automaticaggressivevacuumtopreventwraparoundoftable\"%s.%s.%s\":indexscans:%d\n");elsemsgfmt=_("automaticvacuumtopreventwraparoundoftable\"%s.%s.%s\":indexscans:%d\n");}else{if(aggressive)msgfmt=_("automaticaggressivevacuumoftable\"%s.%s.%s\":indexscans:%d\n");elsemsgfmt=_("automaticvacuumoftable\"%s.%s.%s\":indexscans:%d\n");}appendStringInfo(&buf,msgfmt,get_database_name(MyDatabaseId),get_namespace_name(RelationGetNamespace(onerel)),RelationGetRelationName(onerel),vacrelstats->num_index_scans);appendStringInfo(&buf,_("pages:%uremoved,%uremain,%uskippedduetopins,%uskippedfrozen\n"),vacrelstats->pages_removed,vacrelstats->rel_pages,vacrelstats->pinskipped_pages,vacrelstats->frozenskipped_pages);appendStringInfo(&buf,_("tuples:%.0fremoved,%.0fremain,%.0faredeadbutnotyetremovable,oldestxmin:%u\n"),vacrelstats->tuples_deleted,vacrelstats->new_rel_tuples,vacrelstats->new_dead_tuples,OldestXmin);appendStringInfo(&buf,_("bufferusage:%dhits,%dmisses,%ddirtied\n"),VacuumPageHit,VacuumPageMiss,VacuumPageDirty);appendStringInfo(&buf,_("avgreadrate:%.3fMB/s,avgwriterate:%.3fMB/s\n"),read_rate,write_rate);appendStringInfo(&buf,_("systemusage:%s"),pg_rusage_show(&ru0));ereport(LOG,(errmsg_internal("%s",buf.data)));pfree(buf.data);}}}三、跟踪分析
测试脚本
11:45:37(xdb@[local]:5432)testdb=#vacuumt1;
启动gdb,设置断点
注:PG主线函数名称已改为heap_vacuum_rel,PG 11.1仍为lazy_vacuum_rel
(gdb)cContinuing.Breakpoint1,lazy_vacuum_rel(onerel=0x7f226cd9e9a0,options=1,params=0x7ffe010d5b70,bstrategy=0x1da9708)atvacuumlazy.c:197197TimestampTzstarttime=0;(gdb)
输入参数
relation
(gdb)p*onerel$1={rd_node={spcNode=1663,dbNode=16402,relNode=50820},rd_smgr=0x0,rd_refcnt=1,rd_backend=-1,rd_islocaltemp=false,rd_isnailed=false,rd_isvalid=true,rd_indexvalid=0'\000',rd_statvalid=false,rd_createSubid=0,rd_newRelfilenodeSubid=0,rd_rel=0x7f226cd9ebb8,rd_att=0x7f226cd9ecd0,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=0x0,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=0x1d5a030}(gdb)
vacuum参数
(gdb)p*params$2={freeze_min_age=-1,freeze_table_age=-1,multixact_freeze_min_age=-1,multixact_freeze_table_age=-1,is_wraparound=false,log_min_duration=-1}(gdb)
buffer访问策略对象
(gdb)p*bstrategy$3={btype=BAS_VACUUM,ring_size=32,current=0,current_was_in_ring=false,buffers=0x1da9718}(gdb)(gdb)p*bstrategy->buffers$4=0(gdb)
1.初始化相关变量,如本地变量/日志记录级别/访问策略等
$4=0(gdb)n215if(IsAutoVacuumWorkerProcess()&¶ms->log_min_duration>=0)(gdb)221if(options&VACOPT_VERBOSE)(gdb)224elevel=DEBUG2;(gdb)226pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,(gdb)229vac_strategy=bstrategy;(gdb)
2.调用vacuum_set_xid_limits计算最老的xmin和冻结截止点
返回值均为默认值,其中OldestXmin是当前最小的活动事务ID
231vacuum_set_xid_limits(onerel,(gdb)245aggressive=TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,(gdb)pOldestXmin$5=307590(gdb)pFreezeLimit$6=4245274886(gdb)pxidFullScanLimit$10=4145274886(gdb)pMultiXactCutoff$8=4289967297(gdb)pmxactFullScanLimit$9=4144967297(gdb)
3.判断是否执行全表(不跳过pages)扫描,标记变量为aggressive,值为F
(gdb)n247aggressive|=MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,(gdb)paggressive$11=false(gdb)n249if(options&VACOPT_DISABLE_PAGE_SKIPPING)(gdb)ponerel->rd_rel->relfrozenxid$12=144983(gdb)pxidFullScanLimit$13=4145274886(gdb)
4.初始化统计信息结构体vacrelstats
(gdb)n252vacrelstats=(LVRelStats*)palloc0(sizeof(LVRelStats));(gdb)254vacrelstats->old_rel_pages=onerel->rd_rel->relpages;(gdb)255vacrelstats->old_live_tuples=onerel->rd_rel->reltuples;(gdb)256vacrelstats->num_index_scans=0;(gdb)257vacrelstats->pages_removed=0;(gdb)258vacrelstats->lock_waiter_detected=false;(gdb)261vac_open_indexes(onerel,RowExclusiveLock,&nindexes,&Irel);(gdb)p*vacrelstats$14={hasindex=false,old_rel_pages=75,rel_pages=0,scanned_pages=0,pinskipped_pages=0,frozenskipped_pages=0,tupcount_pages=0,old_live_tuples=10000,new_rel_tuples=0,new_live_tuples=0,new_dead_tuples=0,pages_removed=0,tuples_deleted=0,nonempty_pages=0,num_dead_tuples=0,max_dead_tuples=0,dead_tuples=0x0,num_index_scans=0,latestRemovedXid=0,lock_waiter_detected=false}(gdb)
5.打开索引,执行函数lazy_scan_heap进行vacuuming,关闭索引
(gdb)n262vacrelstats->hasindex=(nindexes>0);(gdb)265lazy_scan_heap(onerel,options,vacrelstats,Irel,nindexes,aggressive);(gdb)268vac_close_indexes(nindexes,Irel,NoLock);(gdb)(gdb)277if((vacrelstats->scanned_pages+vacrelstats->frozenskipped_pages)(gdb)278<vacrelstats->rel_pages)(gdb)277if((vacrelstats->scanned_pages+vacrelstats->frozenskipped_pages)(gdb)284scanned_all_unfrozen=true;(gdb)p*vacrelstats$15={hasindex=true,old_rel_pages=75,rel_pages=75,scanned_pages=75,pinskipped_pages=0,frozenskipped_pages=0,tupcount_pages=75,old_live_tuples=10000,new_rel_tuples=10154,new_live_tuples=10000,new_dead_tuples=154,pages_removed=0,tuples_deleted=0,nonempty_pages=75,num_dead_tuples=0,max_dead_tuples=21825,dead_tuples=0x1db5030,num_index_scans=0,latestRemovedXid=0,lock_waiter_detected=false}(gdb)pvacrelstats->scanned_pages$16=75(gdb)pvacrelstats->frozenskipped_pages$17=0(gdb)pvacrelstats->rel_pages$18=75(gdb)
6.更新pg_class中的统计信息
(gdb)n289if(should_attempt_truncation(vacrelstats))(gdb)293pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,(gdb)317new_rel_pages=vacrelstats->rel_pages;(gdb)318new_live_tuples=vacrelstats->new_live_tuples;(gdb)319if(vacrelstats->tupcount_pages==0&&new_rel_pages>0)(gdb)325visibilitymap_count(onerel,&new_rel_allvisible,NULL);(gdb)326if(new_rel_allvisible>new_rel_pages)(gdb)pnew_rel_allvisible$19=0(gdb)pnew_rel_pages$20=75(gdb)n329new_frozen_xid=scanned_all_unfrozen?FreezeLimit:InvalidTransactionId;(gdb)330new_min_multi=scanned_all_unfrozen?MultiXactCutoff:InvalidMultiXactId;(gdb)336vacrelstats->hasindex,(gdb)332vac_update_relstats(onerel,(gdb)pnew_frozen_xid$21=4245274886(gdb)pnew_min_multi$22=4289967297(gdb)
7.收尾工作
(gdb)n345vacrelstats->new_dead_tuples);(gdb)342pgstat_report_vacuum(RelationGetRelid(onerel),(gdb)343onerel->rd_rel->relisshared,(gdb)342pgstat_report_vacuum(RelationGetRelid(onerel),(gdb)346pgstat_progress_end_command();(gdb)349if(IsAutoVacuumWorkerProcess()&¶ms->log_min_duration>=0)(gdb)
完成调用
411}(gdb)vacuum_rel(relid=50820,relation=0x1cdb8d0,options=1,params=0x7ffe010d5b70)atvacuum.c:15601560AtEOXact_GUC(false,save_nestlevel);(gdb)
到此,关于“PostgreSQL的vacuum过程中heap_vacuum_rel函数分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。