本篇內(nèi)容主要講解“PostgreSQL中vacuum過程分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“PostgreSQL中vacuum過程分析”吧!
目前成都創(chuàng)新互聯(lián)已為成百上千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間、網(wǎng)站改版維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、科爾沁網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
宏定義
Vacuum和Analyze命令選項(xiàng)
/* ---------------------- * Vacuum and Analyze Statements * Vacuum和Analyze命令選項(xiàng) * * Even though these are nominally two statements, it's convenient to use * just one node type for both. Note that at least one of VACOPT_VACUUM * and VACOPT_ANALYZE must be set in options. * 雖然在這里有兩種不同的語句,但只需要使用統(tǒng)一的Node類型即可. * 注意至少VACOPT_VACUUM/VACOPT_ANALYZE在選項(xiàng)中設(shè)置. * ---------------------- */ typedef enum VacuumOption { VACOPT_VACUUM = 1 << 0, /* do VACUUM */ VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */ VACOPT_VERBOSE = 1 << 2, /* print progress info */ VACOPT_FREEZE = 1 << 3, /* FREEZE option */ VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */ VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ } VacuumOption;
VacuumStmt
存儲(chǔ)vacuum命令的option&Relation鏈表
typedef struct VacuumStmt { NodeTag type;//Tag //VacuumOption位標(biāo)記 int options; /* OR of VacuumOption flags */ //VacuumRelation鏈表,如為NIL-->所有Relation. List *rels; /* list of VacuumRelation, or NIL for all */ } VacuumStmt;
VacuumParams
vacuum命令參數(shù)
/* * Parameters customizing behavior of VACUUM and ANALYZE. * 客戶端調(diào)用VACUUM/ANALYZE時(shí)的定制化參數(shù) */ typedef struct VacuumParams { //最小freeze age,-1表示使用默認(rèn) int freeze_min_age; /* min freeze age, -1 to use default */ //掃描整個(gè)table的freeze age int freeze_table_age; /* age at which to scan whole table */ //最小的multixact freeze age,-1表示默認(rèn) int multixact_freeze_min_age; /* min multixact freeze age, -1 to * use default */ //掃描全表的freeze age,-1表示默認(rèn) int multixact_freeze_table_age; /* multixact age at which to scan * whole table */ //是否強(qiáng)制wraparound? bool is_wraparound; /* force a for-wraparound vacuum */ //以毫秒為單位的最小執(zhí)行閾值 int log_min_duration; /* minimum execution threshold in ms at * which verbose logs are activated, -1 * to use default */ } VacuumParams;
VacuumRelation
VACUUM/ANALYZE命令的目標(biāo)表信息
/* * Info about a single target table of VACUUM/ANALYZE. * VACUUM/ANALYZE命令的目標(biāo)表信息. * * If the OID field is set, it always identifies the table to process. * Then the relation field can be NULL; if it isn't, it's used only to report * failure to open/lock the relation. * 如設(shè)置了OID字段,該值通常是將要處理的數(shù)據(jù)表. * 那么關(guān)系字段可以為空;如果不是,則僅用于報(bào)告未能打開/鎖定關(guān)系。 */ typedef struct VacuumRelation { NodeTag type; RangeVar *relation; /* table name to process, or NULL */ Oid oid; /* table's OID; InvalidOid if not looked up */ List *va_cols; /* list of column names, or NIL for all */ } VacuumRelation;
vacuum是VACUUM/ANALYZE命令的內(nèi)部處理入口.
邏輯比較簡單:
1.配置vacuum處理的相關(guān)參數(shù),如命令類型等
2.執(zhí)行相關(guān)檢查
3.構(gòu)造vacuum處理上下文
4.構(gòu)造vacuum需處理的relation鏈表
5.循環(huán)遍歷relation鏈表
5.1 獲取relation
5.2 執(zhí)行vacuum_rel
6.收尾工作
/* * Internal entry point for VACUUM and ANALYZE commands. * VACUUM/ANALYZE命令的內(nèi)部處理入口 * * options is a bitmask of VacuumOption flags, indicating what to do. * options是VacuumOption選項(xiàng)標(biāo)記位,指示應(yīng)該做什么. * * relations, if not NIL, is a list of VacuumRelation to process; otherwise, * we process all relevant tables in the database. For each VacuumRelation, * if a valid OID is supplied, the table with that OID is what to process; * otherwise, the VacuumRelation's RangeVar indicates what to process. * relations,如果不是空指針NIL,那么存儲(chǔ)了待處理的VacuumRelation結(jié)構(gòu)體鏈表. * 如為NIL,將處理數(shù)據(jù)庫中的所有相關(guān)數(shù)據(jù)表. * 對(duì)每一個(gè)VacuumRelation,如提供了有效OID,該OID對(duì)應(yīng)table就會(huì)被處理, * 否則,VacuumRelation的RangeVar指示了如何處理. * * params contains a set of parameters that can be used to customize the * behavior. * params是客戶端定制的參數(shù)集合. * * bstrategy is normally given as NULL, but in autovacuum it can be passed * in to use the same buffer strategy object across multiple vacuum() calls. * bstrategy通常是NULL,但在autovacuum中, * 該參數(shù)可用于指示在多個(gè)vacuum()調(diào)用中使用同樣的緩存strategy object * * isTopLevel should be passed down from ProcessUtility. * isTopLevel通過ProcessUtility向下傳遞 * * It is the caller's responsibility that all parameters are allocated in a * memory context that will not disappear at transaction commit. * 調(diào)用者應(yīng)確保所有的參數(shù)在同一個(gè)內(nèi)存上下文分配內(nèi)存,而不會(huì)在事務(wù)commit時(shí)突然消失. */ void vacuum(int options, List *relations, VacuumParams *params, BufferAccessStrategy bstrategy, bool isTopLevel) { static bool in_vacuum = false;//是否在vacuum const char *stmttype;//語句類型,vacuum?analyze? volatile bool in_outer_xact, use_own_xacts; Assert(params != NULL); stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE"; /* * We cannot run VACUUM inside a user transaction block; if we were inside * a transaction, then our commit- and start-transaction-command calls * would not have the intended effect! There are numerous other subtle * dependencies on this, too. * 不能在用戶事務(wù)塊中運(yùn)行VACUUM,如果我們在事務(wù)塊中, * 那么處理過程中的commit-和start-transaction-command調(diào)用不會(huì)有正確的效果. * 而且還有許多其他微妙的依賴關(guān)系。 * * ANALYZE (without VACUUM) can run either way. * ANALYZE(不帶VACUUM)則沒有此問題. */ if (options & VACOPT_VACUUM) { PreventInTransactionBlock(isTopLevel, stmttype); in_outer_xact = false; } else in_outer_xact = IsInTransactionBlock(isTopLevel); /* * Due to static variables vac_context, anl_context and vac_strategy, * vacuum() is not reentrant. This matters when VACUUM FULL or ANALYZE * calls a hostile index expression that itself calls ANALYZE. * 鑒于vac_context, anl_context and vac_strategy這是變量都是靜態(tài)變量, * 因此vacuum()函數(shù)是不能重入的(狀態(tài)已出現(xiàn)變化). * 在VACUUM FULL或者ANALYZE調(diào)用了hostile index expression, * 而此邏輯又調(diào)用了ANALYZE時(shí)會(huì)出現(xiàn)此情況,務(wù)必注意. */ if (in_vacuum) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("%s cannot be executed from VACUUM or ANALYZE", stmttype))); /* * Sanity check DISABLE_PAGE_SKIPPING option. * 檢查 */ if ((options & VACOPT_FULL) != 0 && (options & VACOPT_DISABLE_PAGE_SKIPPING) != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL"))); /* * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. * 發(fā)送dead objects的統(tǒng)計(jì)信息給收集器,除非我們在autovacuum中 * -- autovacuum.c會(huì)自己做這個(gè)事情. */ if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) pgstat_vacuum_stat(); /* * Create special memory context for cross-transaction storage. * 跨事務(wù)存儲(chǔ),需要?jiǎng)?chuàng)建特別的內(nèi)存上下文. * * Since it is a child of PortalContext, it will go away eventually even * if we suffer an error; there's no need for special abort cleanup logic. * 因?yàn)檫@是PortalContext的子對(duì)象,即使我們犯了錯(cuò)誤,它最終也會(huì)消失;不需要特殊的中止清理邏輯。 */ vac_context = AllocSetContextCreate(PortalContext, "Vacuum", ALLOCSET_DEFAULT_SIZES); /* * If caller didn't give us a buffer strategy object, make one in the * cross-transaction memory context. * 如果調(diào)用者沒有提供buffer strategy object, * 在跨事務(wù)的內(nèi)存上下文中創(chuàng)建一個(gè). */ if (bstrategy == NULL) { MemoryContext old_context = MemoryContextSwitchTo(vac_context); bstrategy = GetAccessStrategy(BAS_VACUUM); MemoryContextSwitchTo(old_context); } vac_strategy = bstrategy; /* * Build list of relation(s) to process, putting any new data in * vac_context for safekeeping. * 構(gòu)建要處理的關(guān)系列表,將所有新數(shù)據(jù)放入vac_context中以進(jìn)行安全(位于vacuum上下文中)保存。 */ if (relations != NIL) { List *newrels = NIL; ListCell *lc; foreach(lc, relations) { VacuumRelation *vrel = lfirst_node(VacuumRelation, lc); List *sublist; MemoryContext old_context; sublist = expand_vacuum_rel(vrel, options); old_context = MemoryContextSwitchTo(vac_context); newrels = list_concat(newrels, sublist); MemoryContextSwitchTo(old_context); } relations = newrels; } else relations = get_all_vacuum_rels(options); /* * Decide whether we need to start/commit our own transactions. * 確定是否需要start/commit自己的事務(wù) * * For VACUUM (with or without ANALYZE): always do so, so that we can * release locks as soon as possible. (We could possibly use the outer * transaction for a one-table VACUUM, but handling TOAST tables would be * problematic.) * 對(duì)于VACUUM(包含或不包含ANALYZE):通常需要這樣處理,以便我們可以盡可能快的釋放鎖. * (對(duì)于一張表的VACUUM,我們可能使用外層事務(wù),但處理TOAST表是會(huì)有問題) * * For ANALYZE (no VACUUM): if inside a transaction block, we cannot * start/commit our own transactions. Also, there's no need to do so if * only processing one relation. For multiple relations when not within a * transaction block, and also in an autovacuum worker, use own * transactions so we can release locks sooner. * 對(duì)于ANALYZE(沒有VACUUM選項(xiàng)):如果在事務(wù)塊中,我們不能start/commit自己的事務(wù). * 同時(shí),如果只需要處理一個(gè)relation,則不需要這樣處理. * 對(duì)于不在一個(gè)事務(wù)塊中的多個(gè)relations/在autovacuum worker中, * 使用自己的事務(wù)以便更快的釋放鎖. */ if (options & VACOPT_VACUUM) use_own_xacts = true; else { Assert(options & VACOPT_ANALYZE); if (IsAutoVacuumWorkerProcess()) use_own_xacts = true; else if (in_outer_xact) use_own_xacts = false; else if (list_length(relations) > 1) use_own_xacts = true; else use_own_xacts = false; } /* * vacuum_rel expects to be entered with no transaction active; it will * start and commit its own transaction. But we are called by an SQL * command, and so we are executing inside a transaction already. We * commit the transaction started in PostgresMain() here, and start * another one before exiting to match the commit waiting for us back in * PostgresMain(). * 在進(jìn)入vacuum_rel前,不希望存在事務(wù)活動(dòng).該函數(shù)會(huì)啟動(dòng)和提交自己的事務(wù). * 但由于我們是通過SQL命令調(diào)用的,因此我們已處于事務(wù)中執(zhí)行. * 在這里我們提交在PostgresMain()中啟動(dòng)的事務(wù), * 并在退出之前啟動(dòng)另一個(gè),以匹配在PostgresMain()中等待我們的提交。 */ if (use_own_xacts) { Assert(!in_outer_xact); /* ActiveSnapshot is not set by autovacuum */ //autovacuum不會(huì)設(shè)置ActiveSnapshot if (ActiveSnapshotSet()) PopActiveSnapshot(); /* matches the StartTransaction in PostgresMain() */ //匹配PostgresMain()中的StartTransaction CommitTransactionCommand(); } /* Turn vacuum cost accounting on or off, and set/clear in_vacuum */ //設(shè)置vacuum成本計(jì)數(shù)on/off,并set/clear in_vacuum參數(shù) PG_TRY(); { ListCell *cur; in_vacuum = true; VacuumCostActive = (VacuumCostDelay > 0); VacuumCostBalance = 0; VacuumPageHit = 0; VacuumPageMiss = 0; VacuumPageDirty = 0; /* * Loop to process each selected relation. * 循環(huán)處理每一個(gè)已選中的relation. */ foreach(cur, relations) { VacuumRelation *vrel = lfirst_node(VacuumRelation, cur); if (options & VACOPT_VACUUM) { //執(zhí)行vacuum處理 if (!vacuum_rel(vrel->oid, vrel->relation, options, params)) continue; } if (options & VACOPT_ANALYZE) { /* * If using separate xacts, start one for analyze. Otherwise, * we can use the outer transaction. * 如果使用獨(dú)立的xacts,為analyze啟動(dòng)一個(gè)何事務(wù). * 否則,我們可以使用外層事務(wù). */ if (use_own_xacts) { //使用自己的事務(wù) StartTransactionCommand(); /* functions in indexes may want a snapshot set */ //快照壓棧 PushActiveSnapshot(GetTransactionSnapshot()); } //分析relation analyze_rel(vrel->oid, vrel->relation, options, params, vrel->va_cols, in_outer_xact, vac_strategy); if (use_own_xacts) { //使用自己的事務(wù),出棧 PopActiveSnapshot(); //提交事務(wù) CommitTransactionCommand(); } } } } PG_CATCH(); { in_vacuum = false; VacuumCostActive = false; PG_RE_THROW(); } PG_END_TRY(); in_vacuum = false; VacuumCostActive = false; /* * Finish up processing. * 完成處理過程 */ if (use_own_xacts) { /* here, we are not in a transaction */ //在這里,沒有處于事務(wù)中 /* * This matches the CommitTransaction waiting for us in * PostgresMain(). * 匹配在PostgresMain()函數(shù)中等待我們的CommitTransaction. */ StartTransactionCommand(); } if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) { /* * Update pg_database.datfrozenxid, and truncate pg_xact if possible. * (autovacuum.c does this for itself.) * 更新pg_database.datfrozenxid,如可能截?cái)鄍g_xact. * (autovacuum.c不會(huì)處理這事情) */ vac_update_datfrozenxid(); } /* * Clean up working storage --- note we must do this after * StartTransactionCommand, else we might be trying to delete the active * context! * 清除工作存儲(chǔ) --- 注意必須在StartTransactionCommand命令后執(zhí)行清除過程, * 否則我們可能會(huì)嘗試刪除活動(dòng)的上下文. */ MemoryContextDelete(vac_context); vac_context = NULL; }
測試腳本
17:19:28 (xdb@[local]:5432)testdb=# vacuum t1;
啟動(dòng)gdb,設(shè)置斷點(diǎn)
(gdb) b vacuum Breakpoint 1 at 0x6b9b8c: file vacuum.c, line 175. (gdb) c Continuing. Breakpoint 1, vacuum (options=1, relations=0x2294988, params=0x7fff403d8880, bstrategy=0x0, isTopLevel=true) at vacuum.c:175 175 Assert(params != NULL); (gdb)
輸入?yún)?shù)
options=1 —> VACOPT_VACUUM
relations=0x2294988,relation鏈表,里面只有一個(gè)item,即t1
params=0x7fff403d8880,默認(rèn)參數(shù)
bstrategy=NULL,
isTopLevel=T,為頂層事務(wù)
(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)
變量賦值并執(zhí)行相關(guān)判斷
(gdb) n 177 stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE"; (gdb) 187 if (options & VACOPT_VACUUM) (gdb) 189 PreventInTransactionBlock(isTopLevel, stmttype); (gdb) 190 in_outer_xact = false; (gdb) 200 if (in_vacuum) (gdb) 209 if ((options & VACOPT_FULL) != 0 && (gdb)
統(tǒng)計(jì)信息
219 if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) (gdb) 220 pgstat_vacuum_stat(); (gdb)
創(chuàng)建并設(shè)置內(nèi)存上下文
(gdb) n 228 vac_context = AllocSetContextCreate(PortalContext, (gdb) 236 if (bstrategy == NULL) (gdb) 238 MemoryContext old_context = MemoryContextSwitchTo(vac_context); (gdb) 240 bstrategy = GetAccessStrategy(BAS_VACUUM); (gdb) 241 MemoryContextSwitchTo(old_context); (gdb) 243 vac_strategy = bstrategy; (gdb) 249 if (relations != NIL) (gdb)
構(gòu)造VacuumRelation鏈表
(gdb) 251 List *newrels = NIL; (gdb) 254 foreach(lc, relations) (gdb) 256 VacuumRelation *vrel = lfirst_node(VacuumRelation, lc); (gdb) 260 sublist = 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) n 261 old_context = MemoryContextSwitchTo(vac_context); (gdb) 262 newrels = list_concat(newrels, sublist); (gdb) 263 MemoryContextSwitchTo(old_context); (gdb) 254 foreach(lc, relations) (gdb) 265 relations = newrels; (gdb)
使用自主事務(wù)
284 if (options & VACOPT_VACUUM) (gdb) 285 use_own_xacts = true; (gdb) 307 if (use_own_xacts) (gdb) 307 if (use_own_xacts) (gdb) 309 Assert(!in_outer_xact); (gdb) 312 if (ActiveSnapshotSet()) (gdb) 313 PopActiveSnapshot(); (gdb) 316 CommitTransactionCommand(); (gdb) 320 PG_TRY(); (gdb)
開始執(zhí)行,設(shè)置vacuum成本計(jì)數(shù)on/off,并set/clear in_vacuum參數(shù)
(gdb) 324 in_vacuum = true; (gdb) 325 VacuumCostActive = (VacuumCostDelay > 0); (gdb) 326 VacuumCostBalance = 0; (gdb) 327 VacuumPageHit = 0; (gdb) 328 VacuumPageMiss = 0; (gdb) 329 VacuumPageDirty = 0; (gdb)
循環(huán)relation,調(diào)用vacuum_rel
334 foreach(cur, relations) (gdb) 336 VacuumRelation *vrel = lfirst_node(VacuumRelation, cur); (gdb) 338 if (options & VACOPT_VACUUM) (gdb) 340 if (!vacuum_rel(vrel->oid, vrel->relation, options, params)) (gdb) 344 if (options & VACOPT_ANALYZE) (gdb) 334 foreach(cur, relations) (gdb) 374 PG_END_TRY(); (gdb)
執(zhí)行收尾工作
(gdb) 376 in_vacuum = false; (gdb) 377 VacuumCostActive = false; (gdb) 382 if (use_own_xacts) (gdb) 390 StartTransactionCommand(); (gdb) 393 if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) (gdb) 399 vac_update_datfrozenxid(); (gdb) 407 MemoryContextDelete(vac_context); (gdb) 408 vac_context = NULL; (gdb)
完成調(diào)用
409 } (gdb) ExecVacuum (vacstmt=0x22949c0, isTopLevel=true) at vacuum.c:142 142 } (gdb)
到此,相信大家對(duì)“PostgreSQL中vacuum過程分析”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!