这篇文章主要介绍“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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。