本篇内容主要讲解“PostgreSQL中vacuum过程分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL中vacuum过程分析”吧!

一、数据结构

宏定义
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;二、源码解读

vacuum是VACUUM/ANALYZE命令的内部处理入口.
逻辑比较简单:
1.配置vacuum处理的相关参数,如命令类型等
2.执行相关检查
3.构造vacuum处理上下文
4.构造vacuum需处理的relation链表
5.循环遍历relation链表
5.1 获取relation
5.2 执行vacuum_rel
6.收尾工作

/**InternalentrypointforVACUUMandANALYZEcommands.*VACUUM/ANALYZE命令的内部处理入口**optionsisabitmaskofVacuumOptionflags,indicatingwhattodo.*options是VacuumOption选项标记位,指示应该做什么.**relations,ifnotNIL,isalistofVacuumRelationtoprocess;otherwise,*weprocessallrelevanttablesinthedatabase.ForeachVacuumRelation,*ifavalidOIDissupplied,thetablewiththatOIDiswhattoprocess;*otherwise,theVacuumRelation'sRangeVarindicateswhattoprocess.*relations,如果不是空指针NIL,那么存储了待处理的VacuumRelation结构体链表.*如为NIL,将处理数据库中的所有相关数据表.*对每一个VacuumRelation,如提供了有效OID,该OID对应table就会被处理,*否则,VacuumRelation的RangeVar指示了如何处理.**paramscontainsasetofparametersthatcanbeusedtocustomizethe*behavior.*params是客户端定制的参数集合.**bstrategyisnormallygivenasNULL,butinautovacuumitcanbepassed*intousethesamebufferstrategyobjectacrossmultiplevacuum()calls.*bstrategy通常是NULL,但在autovacuum中,*该参数可用于指示在多个vacuum()调用中使用同样的缓存strategyobject**isTopLevelshouldbepasseddownfromProcessUtility.*isTopLevel通过ProcessUtility向下传递**Itisthecaller'sresponsibilitythatallparametersareallocatedina*memorycontextthatwillnotdisappearattransactioncommit.*调用者应确保所有的参数在同一个内存上下文分配内存,而不会在事务commit时突然消失.*/voidvacuum(intoptions,List*relations,VacuumParams*params,BufferAccessStrategybstrategy,boolisTopLevel){staticboolin_vacuum=false;//是否在vacuumconstchar*stmttype;//语句类型,vacuum?analyze?volatileboolin_outer_xact,use_own_xacts;Assert(params!=NULL);stmttype=(options&VACOPT_VACUUM)?"VACUUM":"ANALYZE";/**WecannotrunVACUUMinsideausertransactionblock;ifwewereinside*atransaction,thenourcommit-andstart-transaction-commandcalls*wouldnothavetheintendedeffect!Therearenumerousothersubtle*dependenciesonthis,too.*不能在用户事务块中运行VACUUM,如果我们在事务块中,*那么处理过程中的commit-和start-transaction-command调用不会有正确的效果.*而且还有许多其他微妙的依赖关系。**ANALYZE(withoutVACUUM)canruneitherway.*ANALYZE(不带VACUUM)则没有此问题.*/if(options&VACOPT_VACUUM){PreventInTransactionBlock(isTopLevel,stmttype);in_outer_xact=false;}elsein_outer_xact=IsInTransactionBlock(isTopLevel);/**Duetostaticvariablesvac_context,anl_contextandvac_strategy,*vacuum()isnotreentrant.ThismatterswhenVACUUMFULLorANALYZE*callsahostileindexexpressionthatitselfcallsANALYZE.*鉴于vac_context,anl_contextandvac_strategy这是变量都是静态变量,*因此vacuum()函数是不能重入的(状态已出现变化).*在VACUUMFULL或者ANALYZE调用了hostileindexexpression,*而此逻辑又调用了ANALYZE时会出现此情况,务必注意.*/if(in_vacuum)ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("%scannotbeexecutedfromVACUUMorANALYZE",stmttype)));/**SanitycheckDISABLE_PAGE_SKIPPINGoption.*检查*/if((options&VACOPT_FULL)!=0&&(options&VACOPT_DISABLE_PAGE_SKIPPING)!=0)ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("VACUUMoptionDISABLE_PAGE_SKIPPINGcannotbeusedwithFULL")));/**Sendinfoaboutdeadobjectstothestatisticscollector,unlessweare*inautovacuum---autovacuum.cdoesthisforitself.*发送deadobjects的统计信息给收集器,除非我们在autovacuum中*--autovacuum.c会自己做这个事情.*/if((options&VACOPT_VACUUM)&&!IsAutoVacuumWorkerProcess())pgstat_vacuum_stat();/**Createspecialmemorycontextforcross-transactionstorage.*跨事务存储,需要创建特别的内存上下文.**SinceitisachildofPortalContext,itwillgoawayeventuallyeven*ifwesufferanerror;there'snoneedforspecialabortcleanuplogic.*因为这是PortalContext的子对象,即使我们犯了错误,它最终也会消失;不需要特殊的中止清理逻辑。*/vac_context=AllocSetContextCreate(PortalContext,"Vacuum",ALLOCSET_DEFAULT_SIZES);/**Ifcallerdidn'tgiveusabufferstrategyobject,makeoneinthe*cross-transactionmemorycontext.*如果调用者没有提供bufferstrategyobject,*在跨事务的内存上下文中创建一个.*/if(bstrategy==NULL){MemoryContextold_context=MemoryContextSwitchTo(vac_context);bstrategy=GetAccessStrategy(BAS_VACUUM);MemoryContextSwitchTo(old_context);}vac_strategy=bstrategy;/**Buildlistofrelation(s)toprocess,puttinganynewdatain*vac_contextforsafekeeping.*构建要处理的关系列表,将所有新数据放入vac_context中以进行安全(位于vacuum上下文中)保存。*/if(relations!=NIL){List*newrels=NIL;ListCell*lc;foreach(lc,relations){VacuumRelation*vrel=lfirst_node(VacuumRelation,lc);List*sublist;MemoryContextold_context;sublist=expand_vacuum_rel(vrel,options);old_context=MemoryContextSwitchTo(vac_context);newrels=list_concat(newrels,sublist);MemoryContextSwitchTo(old_context);}relations=newrels;}elserelations=get_all_vacuum_rels(options);/**Decidewhetherweneedtostart/commitourowntransactions.*确定是否需要start/commit自己的事务**ForVACUUM(withorwithoutANALYZE):alwaysdoso,sothatwecan*releaselocksassoonaspossible.(Wecouldpossiblyusetheouter*transactionforaone-tableVACUUM,buthandlingTOASTtableswouldbe*problematic.)*对于VACUUM(包含或不包含ANALYZE):通常需要这样处理,以便我们可以尽可能快的释放锁.*(对于一张表的VACUUM,我们可能使用外层事务,但处理TOAST表是会有问题)**ForANALYZE(noVACUUM):ifinsideatransactionblock,wecannot*start/commitourowntransactions.Also,there'snoneedtodosoif*onlyprocessingonerelation.Formultiplerelationswhennotwithina*transactionblock,andalsoinanautovacuumworker,useown*transactionssowecanreleaselockssooner.*对于ANALYZE(没有VACUUM选项):如果在事务块中,我们不能start/commit自己的事务.*同时,如果只需要处理一个relation,则不需要这样处理.*对于不在一个事务块中的多个relations/在autovacuumworker中,*使用自己的事务以便更快的释放锁.*/if(options&VACOPT_VACUUM)use_own_xacts=true;else{Assert(options&VACOPT_ANALYZE);if(IsAutoVacuumWorkerProcess())use_own_xacts=true;elseif(in_outer_xact)use_own_xacts=false;elseif(list_length(relations)>1)use_own_xacts=true;elseuse_own_xacts=false;}/**vacuum_relexpectstobeenteredwithnotransactionactive;itwill*startandcommititsowntransaction.ButwearecalledbyanSQL*command,andsoweareexecutinginsideatransactionalready.We*committhetransactionstartedinPostgresMain()here,andstart*anotheronebeforeexitingtomatchthecommitwaitingforusbackin*PostgresMain().*在进入vacuum_rel前,不希望存在事务活动.该函数会启动和提交自己的事务.*但由于我们是通过SQL命令调用的,因此我们已处于事务中执行.*在这里我们提交在PostgresMain()中启动的事务,*并在退出之前启动另一个,以匹配在PostgresMain()中等待我们的提交。*/if(use_own_xacts){Assert(!in_outer_xact);/*ActiveSnapshotisnotsetbyautovacuum*///autovacuum不会设置ActiveSnapshotif(ActiveSnapshotSet())PopActiveSnapshot();/*matchestheStartTransactioninPostgresMain()*///匹配PostgresMain()中的StartTransactionCommitTransactionCommand();}/*Turnvacuumcostaccountingonoroff,andset/clearin_vacuum*///设置vacuum成本计数on/off,并set/clearin_vacuum参数PG_TRY();{ListCell*cur;in_vacuum=true;VacuumCostActive=(VacuumCostDelay>0);VacuumCostBalance=0;VacuumPageHit=0;VacuumPageMiss=0;VacuumPageDirty=0;/**Looptoprocesseachselectedrelation.*循环处理每一个已选中的relation.*/foreach(cur,relations){VacuumRelation*vrel=lfirst_node(VacuumRelation,cur);if(options&VACOPT_VACUUM){//执行vacuum处理if(!vacuum_rel(vrel->oid,vrel->relation,options,params))continue;}if(options&VACOPT_ANALYZE){/**Ifusingseparatexacts,startoneforanalyze.Otherwise,*wecanusetheoutertransaction.*如果使用独立的xacts,为analyze启动一个何事务.*否则,我们可以使用外层事务.*/if(use_own_xacts){//使用自己的事务StartTransactionCommand();/*functionsinindexesmaywantasnapshotset*///快照压栈PushActiveSnapshot(GetTransactionSnapshot());}//分析relationanalyze_rel(vrel->oid,vrel->relation,options,params,vrel->va_cols,in_outer_xact,vac_strategy);if(use_own_xacts){//使用自己的事务,出栈PopActiveSnapshot();//提交事务CommitTransactionCommand();}}}}PG_CATCH();{in_vacuum=false;VacuumCostActive=false;PG_RE_THROW();}PG_END_TRY();in_vacuum=false;VacuumCostActive=false;/**Finishupprocessing.*完成处理过程*/if(use_own_xacts){/*here,wearenotinatransaction*///在这里,没有处于事务中/**ThismatchestheCommitTransactionwaitingforusin*PostgresMain().*匹配在PostgresMain()函数中等待我们的CommitTransaction.*/StartTransactionCommand();}if((options&VACOPT_VACUUM)&&!IsAutoVacuumWorkerProcess()){/**Updatepg_database.datfrozenxid,andtruncatepg_xactifpossible.*(autovacuum.cdoesthisforitself.)*更新pg_database.datfrozenxid,如可能截断pg_xact.*(autovacuum.c不会处理这事情)*/vac_update_datfrozenxid();}/**Cleanupworkingstorage---notewemustdothisafter*StartTransactionCommand,elsewemightbetryingtodeletetheactive*context!*清除工作存储---注意必须在StartTransactionCommand命令后执行清除过程,*否则我们可能会尝试删除活动的上下文.*/MemoryContextDelete(vac_context);vac_context=NULL;}三、跟踪分析

测试脚本

17:19:28(xdb@[local]:5432)testdb=#vacuumt1;

启动gdb,设置断点

(gdb)bvacuumBreakpoint1at0x6b9b8c:filevacuum.c,line175.(gdb)cContinuing.Breakpoint1,vacuum(options=1,relations=0x2294988,params=0x7fff403d8880,bstrategy=0x0,isTopLevel=true)atvacuum.c:175175Assert(params!=NULL);(gdb)

输入参数
options=1 —> VACOPT_VACUUM
relations=0x2294988,relation链表,里面只有一个item,即t1
params=0x7fff403d8880,默认参数
bstrategy=NULL,
isTopLevel=T,为顶层事务

(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)

变量赋值并执行相关判断

(gdb)n177stmttype=(options&VACOPT_VACUUM)?"VACUUM":"ANALYZE";(gdb)187if(options&VACOPT_VACUUM)(gdb)189PreventInTransactionBlock(isTopLevel,stmttype);(gdb)190in_outer_xact=false;(gdb)200if(in_vacuum)(gdb)209if((options&VACOPT_FULL)!=0&&(gdb)

统计信息

219if((options&VACOPT_VACUUM)&&!IsAutoVacuumWorkerProcess())(gdb)220pgstat_vacuum_stat();(gdb)

创建并设置内存上下文

(gdb)n228vac_context=AllocSetContextCreate(PortalContext,(gdb)236if(bstrategy==NULL)(gdb)238MemoryContextold_context=MemoryContextSwitchTo(vac_context);(gdb)240bstrategy=GetAccessStrategy(BAS_VACUUM);(gdb)241MemoryContextSwitchTo(old_context);(gdb)243vac_strategy=bstrategy;(gdb)249if(relations!=NIL)(gdb)

构造VacuumRelation链表

(gdb)251List*newrels=NIL;(gdb)254foreach(lc,relations)(gdb)256VacuumRelation*vrel=lfirst_node(VacuumRelation,lc);(gdb)260sublist=expand_vacuum_rel(vrel);(gdb)p*vrel$3={type=T_VacuumRelation,relation=0x22948d0,oid=0,va_cols=0x0}(gdb)p*vrel->relation$4={type=T_RangeVar,catalogname=0x0,schemaname=0x0,relname=0x22948b0"t1",inh=true,relpersistence=112'p',alias=0x0,location=7}(gdb)(gdb)n261old_context=MemoryContextSwitchTo(vac_context);(gdb)262newrels=list_concat(newrels,sublist);(gdb)263MemoryContextSwitchTo(old_context);(gdb)254foreach(lc,relations)(gdb)265relations=newrels;(gdb)

使用自主事务

284if(options&VACOPT_VACUUM)(gdb)285use_own_xacts=true;(gdb)307if(use_own_xacts)(gdb)307if(use_own_xacts)(gdb)309Assert(!in_outer_xact);(gdb)312if(ActiveSnapshotSet())(gdb)313PopActiveSnapshot();(gdb)316CommitTransactionCommand();(gdb)320PG_TRY();(gdb)

开始执行,设置vacuum成本计数on/off,并set/clear in_vacuum参数

(gdb)324in_vacuum=true;(gdb)325VacuumCostActive=(VacuumCostDelay>0);(gdb)326VacuumCostBalance=0;(gdb)327VacuumPageHit=0;(gdb)328VacuumPageMiss=0;(gdb)329VacuumPageDirty=0;(gdb)

循环relation,调用vacuum_rel

334foreach(cur,relations)(gdb)336VacuumRelation*vrel=lfirst_node(VacuumRelation,cur);(gdb)338if(options&VACOPT_VACUUM)(gdb)340if(!vacuum_rel(vrel->oid,vrel->relation,options,params))(gdb)344if(options&VACOPT_ANALYZE)(gdb)334foreach(cur,relations)(gdb)374PG_END_TRY();(gdb)

执行收尾工作

(gdb)376in_vacuum=false;(gdb)377VacuumCostActive=false;(gdb)382if(use_own_xacts)(gdb)390StartTransactionCommand();(gdb)393if((options&VACOPT_VACUUM)&&!IsAutoVacuumWorkerProcess())(gdb)399vac_update_datfrozenxid();(gdb)407MemoryContextDelete(vac_context);(gdb)408vac_context=NULL;(gdb)

完成调用

409}(gdb)ExecVacuum(vacstmt=0x22949c0,isTopLevel=true)atvacuum.c:142142}(gdb)

到此,相信大家对“PostgreSQL中vacuum过程分析”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!