您好,登錄后才能下訂單哦!
這篇文章主要介紹“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命令選項
/* ---------------------- * Vacuum and Analyze Statements * Vacuum和Analyze命令選項 * * 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. * 雖然在這里有兩種不同的語句,但只需要使用統一的Node類型即可. * 注意至少VACOPT_VACUUM/VACOPT_ANALYZE在選項中設置. * ---------------------- */ 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
存儲vacuum命令的option&Relation鏈表
typedef struct VacuumStmt { NodeTag type;//Tag //VacuumOption位標記 int options; /* OR of VacuumOption flags */ //VacuumRelation鏈表,如為NIL-->所有Relation. List *rels; /* list of VacuumRelation, or NIL for all */ } VacuumStmt;
VacuumParams
vacuum命令參數
/* * Parameters customizing behavior of VACUUM and ANALYZE. * 客戶端調用VACUUM/ANALYZE時的定制化參數 */ typedef struct VacuumParams { //最小freeze age,-1表示使用默認 int freeze_min_age; /* min freeze age, -1 to use default */ //掃描整個table的freeze age int freeze_table_age; /* age at which to scan whole table */ //最小的multixact freeze age,-1表示默認 int multixact_freeze_min_age; /* min multixact freeze age, -1 to * use default */ //掃描全表的freeze age,-1表示默認 int multixact_freeze_table_age; /* multixact age at which to scan * whole table */ //是否強制wraparound? bool is_wraparound; /* force a for-wraparound vacuum */ //以毫秒為單位的最小執行閾值 int log_min_duration; /* minimum execution threshold in ms at * which verbose logs are activated, -1 * to use default */ } VacuumParams;
VacuumRelation
VACUUM/ANALYZE命令的目標表信息
/* * Info about a single target table of VACUUM/ANALYZE. * VACUUM/ANALYZE命令的目標表信息. * * 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. * 如設置了OID字段,該值通常是將要處理的數據表. * 那么關系字段可以為空;如果不是,則僅用于報告未能打開/鎖定關系。 */ 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;
BufferAccessStrategy
Buffer訪問策略對象
/* * Buffer identifiers. * Buffer標識符 * * Zero is invalid, positive is the index of a shared buffer (1..NBuffers), * negative is the index of a local buffer (-1 .. -NLocBuffer). * 0表示無效,正整數表示共享buffer的索引(1..N), * 負數是本地buffer的索引(-1..-N) */ typedef int Buffer; #define InvalidBuffer 0 /* * Buffer access strategy objects. * Buffer訪問策略對象 * * BufferAccessStrategyData is private to freelist.c * BufferAccessStrategyData對freelist.c來說是私有的 */ typedef struct BufferAccessStrategyData *BufferAccessStrategy; /* * Private (non-shared) state for managing a ring of shared buffers to re-use. * This is currently the only kind of BufferAccessStrategy object, but someday * we might have more kinds. * 私有狀態,用于管理可重用的環形緩沖區. * 目前只有這么一種緩沖區訪問策略對象,但未來某一天可以擁有更多. */ typedef struct BufferAccessStrategyData { /* Overall strategy type */ //全局的策略類型 BufferAccessStrategyType btype; /* Number of elements in buffers[] array */ //buffers[]中的元素個數 int ring_size; /* * Index of the "current" slot in the ring, ie, the one most recently * returned by GetBufferFromRing. * 環形緩沖區中當前slot的索引,最近訪問的通過函數GetBufferFromRing返回. */ int current; /* * True if the buffer just returned by StrategyGetBuffer had been in the * ring already. * 如正好通過StrategyGetBuffer返回的buffer已在環形緩沖區中,則返回T */ bool current_was_in_ring; /* * Array of buffer numbers. InvalidBuffer (that is, zero) indicates we * have not yet selected a buffer for this ring slot. For allocation * simplicity this is palloc'd together with the fixed fields of the * struct. * buffer編號數組. * InvalidBuffer(即:0)表示我們還沒有為該slot選擇buffer. * 為了分配的簡單性,這是palloc'd與結構的固定字段。 */ Buffer buffers[FLEXIBLE_ARRAY_MEMBER]; } BufferAccessStrategyData; //Block結構體指針 typedef void *Block; /* Possible arguments for GetAccessStrategy() */ //GetAccessStrategy()函數可取值的參數 typedef enum BufferAccessStrategyType { //常規的隨機訪問 BAS_NORMAL, /* Normal random access */ //大規模的只讀掃描 BAS_BULKREAD, /* Large read-only scan (hint bit updates are * ok) */ //大量的多塊寫(如 COPY IN) BAS_BULKWRITE, /* Large multi-block write (e.g. COPY IN) */ //VACUUM BAS_VACUUM /* VACUUM */ } BufferAccessStrategyType;
LVRelStats
typedef struct LVRelStats { /* hasindex = true means two-pass strategy; false means one-pass */ //T表示two-pass strategy,F表示one-pass strategy bool hasindex; /* Overall statistics about rel */ //rel的全局統計信息 //pg_class.relpages的上一個值 BlockNumber old_rel_pages; /* previous value of pg_class.relpages */ //pages的總數 BlockNumber rel_pages; /* total number of pages */ //掃描的pages BlockNumber scanned_pages; /* number of pages we examined */ //由于pin跳過的pages BlockNumber pinskipped_pages; /* # of pages we skipped due to a pin */ //跳過的frozen pages BlockNumber frozenskipped_pages; /* # of frozen pages we skipped */ //計算其元組的pages BlockNumber tupcount_pages; /* pages whose tuples we counted */ //pg_class.reltuples的前值 double old_live_tuples; /* previous value of pg_class.reltuples */ //新估算的總元組數 double new_rel_tuples; /* new estimated total # of tuples */ //新估算的存活元組數 double new_live_tuples; /* new estimated total # of live tuples */ //新估算的廢棄元組數 double new_dead_tuples; /* new estimated total # of dead tuples */ //已清除的pages BlockNumber pages_removed; //已刪除的tuples double tuples_deleted; //實際上是非空page + 1 BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */ /* List of TIDs of tuples we intend to delete */ /* NB: this list is ordered by TID address */ //將要刪除的元組TIDs鏈表 //注意:該鏈表已使用TID地址排序 //當前的入口/條目數 int num_dead_tuples; /* current # of entries */ //數組中已分配的slots(最大已廢棄元組數) int max_dead_tuples; /* # slots allocated in array */ //ItemPointerData數組 ItemPointer dead_tuples; /* array of ItemPointerData */ //掃描的索引數 int num_index_scans; //最后被清除的事務ID TransactionId latestRemovedXid; //是否存在waiter? bool lock_waiter_detected; } LVRelStats;
PGRUsage
pg_rusage_init/pg_rusage_show的狀態結構體
/* State structure for pg_rusage_init/pg_rusage_show */ //pg_rusage_init/pg_rusage_show的狀態結構體 typedef struct PGRUsage { struct timeval tv; struct rusage ru; } PGRUsage; struct rusage { struct timeval ru_utime; /* user time used */ struct timeval ru_stime; /* system time used */ }; struct timeval { __time_t tv_sec; /* 秒數.Seconds. */ __suseconds_t tv_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() -- perform VACUUM for one heap relation * heap_vacuum_rel() -- 為heap relation執行VACUUM * * This routine vacuums a single heap, cleans out its indexes, and * updates its relpages and reltuples statistics. * 該處理過程vacuum一個單獨的heap,清除索引并更新relpages和reltuples統計信息. * * At entry, we have already established a transaction and opened * and locked the relation. * 在該調用入口,我們已經給創建了事務并且已經打開&鎖定了relation. */ void heap_vacuum_rel(Relation onerel, int options, VacuumParams *params, BufferAccessStrategy bstrategy) { LVRelStats *vacrelstats;//統計信息 Relation *Irel;//關系指針 int nindexes; PGRUsage ru0;//狀態結構體 TimestampTz starttime = 0;//時間戳 long secs;//秒數 int usecs;//微秒數 double read_rate,//讀比率 write_rate;//寫比率 //是否掃描所有未凍結的pages? bool aggressive; /* should we scan all unfrozen pages? */ //實際上是否掃描了所有這樣的pages? bool scanned_all_unfrozen; /* actually scanned all such pages? */ TransactionId xidFullScanLimit; MultiXactId mxactFullScanLimit; BlockNumber new_rel_pages; BlockNumber new_rel_allvisible; double new_live_tuples; TransactionId new_frozen_xid; MultiXactId new_min_multi; Assert(params != NULL); /* measure elapsed time iff autovacuum logging requires it */ //如autovacuum日志記錄需要,則測量耗費的時間 if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0) { pg_rusage_init(&ru0); starttime = GetCurrentTimestamp(); } if (options & VACOPT_VERBOSE) //需要VERBOSE elevel = INFO; else elevel = DEBUG2; pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, RelationGetRelid(onerel)); vac_strategy = bstrategy; //計算最老的xmin和凍結截止點 //輸出:OldestXmin/FreezeLimit/FreezeLimit/MultiXactCutoff/mxactFullScanLimit vacuum_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); /* * We request an aggressive scan if the table's frozen Xid is now older * than or equal to the requested Xid full-table scan limit; or if the * table's minimum MultiXactId is older than or equal to the requested * mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified. * 如果表的frozen Xid現在大于或等于請求的Xid全表掃描限制,則請求進行主動掃描; * 或者如果表的最小MultiXactId大于或等于請求的mxid全表掃描限制; * 或者,如果指定了disable_page_skip。 */ //比較onerel->rd_rel->relfrozenxid & xidFullScanLimit //如小于等于,則aggressive為T,否則為F aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid, xidFullScanLimit); //比較onerel->rd_rel->relminmxid &mxactFullScanLimit //如小于等于,則aggressive為T //否則aggressive原值為T,則為T,否則為F aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid, mxactFullScanLimit); if (options & VACOPT_DISABLE_PAGE_SKIPPING) //禁用跳過頁,則強制為T aggressive = 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; /* Open all indexes of the relation */ //打開該relation所有的索引 vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel); vacrelstats->hasindex = (nindexes > 0); /* Do the vacuuming */ //執行vacuuming lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive); /* Done with indexes */ //已完成index的處理 vac_close_indexes(nindexes, Irel, NoLock); /* * Compute whether we actually scanned the all unfrozen pages. If we did, * we can adjust relfrozenxid and relminmxid. * 計算我們實際上是否掃描了所有unfrozen pages. * 如果掃描了,則需要調整relfrozenxid和relminmxid. * * NB: We need to check this before truncating the relation, because that * will change ->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,則為T scanned_all_unfrozen = true; /* * Optionally truncate the relation. * 可選的,截斷relation */ if (should_attempt_truncation(vacrelstats)) lazy_truncate_heap(onerel, vacrelstats); /* Report that we are now doing final cleanup */ //通知其他進程,正在進行最后的清理 pgstat_progress_update_param(PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_FINAL_CLEANUP); /* * Update statistics in pg_class. * 更新pg_class中的統計信息. * * A corner case here is that if we scanned no pages at all because every * page is all-visible, we should not update relpages/reltuples, because * we have no new information to contribute. In particular this keeps us * from replacing relpages=reltuples=0 (which means "unknown tuple * density") with nonzero relpages and reltuples=0 (which means "zero * tuple density") unless there's some actual evidence for the latter. * 這里的一個極端情況是,如果每個頁面都是可見的,這時候根本沒有掃描任何頁面, * 那么就不應該更新relpages/reltuples,因為我們沒有新信息可以更新。 * 特別地,這阻止我們將relpages=reltuple =0(這意味著“未知的元組密度”)替換 * 為非零relpages和reltuple=0(這意味著“零元組密度”), * 除非有關于后者的一些實際的證據。 * * It's important that we use tupcount_pages and not scanned_pages for the * check described above; scanned_pages counts pages where we could not * get cleanup lock, and which were processed only for frozenxid purposes. * 對于上面描述的檢查,使用tupcount_pages而不是scanned_pages是很重要的; * scanned_pages對無法獲得清理鎖的頁面進行計數,這些頁面僅用于frozenxid目的。 * * We do update relallvisible even in the corner case, since if the table * is all-visible we'd definitely like to know that. But clamp the value * to be not more than what we're setting relpages to. * 即使在極端情況下,我們也會更新relallvisible,因為如果表是all-visible的,那我們肯定想知道這個. * 但是不要超過我們設置relpages的值。 * * Also, don't change relfrozenxid/relminmxid if we skipped any pages, * since then we don't know for certain that all tuples have a newer xmin. * 同時,如果我們跳過了所有的頁面,不能更新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); /* report results to the stats collector, too */ //同時,發送結果給統計收集器 pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared, new_live_tuples, vacrelstats->new_dead_tuples); pgstat_progress_end_command(); /* and log the action if appropriate */ //并在適當的情況下記錄操作 if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0) { //autovacuum && 參數log_min_duration >= 0 TimestampTz endtime = GetCurrentTimestamp(); if (params->log_min_duration == 0 || TimestampDifferenceExceeds(starttime, endtime, params->log_min_duration)) { StringInfoData buf; 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); } /* * This is pretty messy, but we split it up so that we can skip * emitting individual parts of the message when not applicable. */ initStringInfo(&buf); if (params->is_wraparound) { if (aggressive) msgfmt = _("automatic aggressive vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n"); else msgfmt = _("automatic vacuum to prevent wraparound of table \"%s.%s.%s\": index scans: %d\n"); } else { if (aggressive) msgfmt = _("automatic aggressive vacuum of table \"%s.%s.%s\": index scans: %d\n"); else msgfmt = _("automatic vacuum of table \"%s.%s.%s\": index scans: %d\n"); } appendStringInfo(&buf, msgfmt, get_database_name(MyDatabaseId), get_namespace_name(RelationGetNamespace(onerel)), RelationGetRelationName(onerel), vacrelstats->num_index_scans); appendStringInfo(&buf, _("pages: %u removed, %u remain, %u skipped due to pins, %u skipped frozen\n"), vacrelstats->pages_removed, vacrelstats->rel_pages, vacrelstats->pinskipped_pages, vacrelstats->frozenskipped_pages); appendStringInfo(&buf, _("tuples: %.0f removed, %.0f remain, %.0f are dead but not yet removable, oldest xmin: %u\n"), vacrelstats->tuples_deleted, vacrelstats->new_rel_tuples, vacrelstats->new_dead_tuples, OldestXmin); appendStringInfo(&buf, _("buffer usage: %d hits, %d misses, %d dirtied\n"), VacuumPageHit, VacuumPageMiss, VacuumPageDirty); appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"), read_rate, write_rate); appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0)); ereport(LOG, (errmsg_internal("%s", buf.data))); pfree(buf.data); } } }
測試腳本
11:45:37 (xdb@[local]:5432)testdb=# vacuum t1;
啟動gdb,設置斷點
注:PG主線函數名稱已改為heap_vacuum_rel,PG 11.1仍為lazy_vacuum_rel
(gdb) c Continuing. Breakpoint 1, lazy_vacuum_rel (onerel=0x7f226cd9e9a0, options=1, params=0x7ffe010d5b70, bstrategy=0x1da9708) at vacuumlazy.c:197 197 TimestampTz starttime = 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) n 215 if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0) (gdb) 221 if (options & VACOPT_VERBOSE) (gdb) 224 elevel = DEBUG2; (gdb) 226 pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, (gdb) 229 vac_strategy = bstrategy; (gdb)
2.調用vacuum_set_xid_limits計算最老的xmin和凍結截止點
返回值均為默認值,其中OldestXmin是當前最小的活動事務ID
231 vacuum_set_xid_limits(onerel, (gdb) 245 aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid, (gdb) p OldestXmin $5 = 307590 (gdb) p FreezeLimit $6 = 4245274886 (gdb) p xidFullScanLimit $10 = 4145274886 (gdb) p MultiXactCutoff $8 = 4289967297 (gdb) p mxactFullScanLimit $9 = 4144967297 (gdb)
3.判斷是否執行全表(不跳過pages)掃描,標記變量為aggressive,值為F
(gdb) n 247 aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid, (gdb) p aggressive $11 = false (gdb) n 249 if (options & VACOPT_DISABLE_PAGE_SKIPPING) (gdb) p onerel->rd_rel->relfrozenxid $12 = 144983 (gdb) p xidFullScanLimit $13 = 4145274886 (gdb)
4.初始化統計信息結構體vacrelstats
(gdb) n 252 vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats)); (gdb) 254 vacrelstats->old_rel_pages = onerel->rd_rel->relpages; (gdb) 255 vacrelstats->old_live_tuples = onerel->rd_rel->reltuples; (gdb) 256 vacrelstats->num_index_scans = 0; (gdb) 257 vacrelstats->pages_removed = 0; (gdb) 258 vacrelstats->lock_waiter_detected = false; (gdb) 261 vac_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) n 262 vacrelstats->hasindex = (nindexes > 0); (gdb) 265 lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive); (gdb) 268 vac_close_indexes(nindexes, Irel, NoLock); (gdb) (gdb) 277 if ((vacrelstats->scanned_pages + vacrelstats->frozenskipped_pages) (gdb) 278 < vacrelstats->rel_pages) (gdb) 277 if ((vacrelstats->scanned_pages + vacrelstats->frozenskipped_pages) (gdb) 284 scanned_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) p vacrelstats->scanned_pages $16 = 75 (gdb) p vacrelstats->frozenskipped_pages $17 = 0 (gdb) p vacrelstats->rel_pages $18 = 75 (gdb)
6.更新pg_class中的統計信息
(gdb) n 289 if (should_attempt_truncation(vacrelstats)) (gdb) 293 pgstat_progress_update_param(PROGRESS_VACUUM_PHASE, (gdb) 317 new_rel_pages = vacrelstats->rel_pages; (gdb) 318 new_live_tuples = vacrelstats->new_live_tuples; (gdb) 319 if (vacrelstats->tupcount_pages == 0 && new_rel_pages > 0) (gdb) 325 visibilitymap_count(onerel, &new_rel_allvisible, NULL); (gdb) 326 if (new_rel_allvisible > new_rel_pages) (gdb) p new_rel_allvisible $19 = 0 (gdb) p new_rel_pages $20 = 75 (gdb) n 329 new_frozen_xid = scanned_all_unfrozen ? FreezeLimit : InvalidTransactionId; (gdb) 330 new_min_multi = scanned_all_unfrozen ? MultiXactCutoff : InvalidMultiXactId; (gdb) 336 vacrelstats->hasindex, (gdb) 332 vac_update_relstats(onerel, (gdb) p new_frozen_xid $21 = 4245274886 (gdb) p new_min_multi $22 = 4289967297 (gdb)
7.收尾工作
(gdb) n 345 vacrelstats->new_dead_tuples); (gdb) 342 pgstat_report_vacuum(RelationGetRelid(onerel), (gdb) 343 onerel->rd_rel->relisshared, (gdb) 342 pgstat_report_vacuum(RelationGetRelid(onerel), (gdb) 346 pgstat_progress_end_command(); (gdb) 349 if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0) (gdb)
完成調用
411 } (gdb) vacuum_rel (relid=50820, relation=0x1cdb8d0, options=1, params=0x7ffe010d5b70) at vacuum.c:1560 1560 AtEOXact_GUC(false, save_nestlevel); (gdb)
到此,關于“PostgreSQL的vacuum過程中heap_vacuum_rel函數分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。