Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Interpretation of PostgreSQL Source Code (130)-MVCC#14 (vacuum procedure-lazy_scan_heap function)

2025-04-07 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)06/01 Report--

This section briefly introduces the processing flow of manual vacuum execution by PostgreSQL, and mainly analyzes the implementation logic of the ExecVacuum- > vacuum- > vacuum_rel- > heap_vacuum_rel- > lazy_scan_heap function, which scans open heap relation and cleans up every page in the heap.

I. data structure

Macro definition

Vacuum and Analyze command options

/ *-- * Vacuum and Analyze Statements * Vacuum and Analyze command options * * 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. Although there are two different statements here, you only need to use a uniform Node type. * Note that at least VACOPT_VACUUM/VACOPT_ANALYZE is set in the options. *-- * / typedef enum VacuumOption {VACOPT_VACUUM = 1 rel_pages = nblocks; vacrelstats- > scanned_pages = 0; vacrelstats- > tupcount_pages = 0; vacrelstats- > nonempty_pages = 0; vacrelstats- > latestRemovedXid = InvalidTransactionId; / / each block is recorded separately lazy_space_alloc (vacrelstats, nblocks) / / allocate memory space for frozen array frozen = palloc (sizeof (xl_heap_freeze_tuple) * MaxHeapTuplesPerPage); / * Report that we're scanning the heap, advertising total # of blocks * / / reports scanning heap and broadcasts the total number of blocks / / PROGRESS_VACUUM_PHASE_SCAN_HEAP status initprog_val [0] = PROGRESS_VACUUM_PHASE_SCAN_HEAP; initprog_val [1] = nblocks / / Total number of blocks initprog_val [2] = vacrelstats- > max_dead_tuples;// maximum number of abandoned tuples pgstat_progress_update_multi_param (3, initprog_index, initprog_val); / * * Except when aggressive is set, we want to skip pages that are * all-visible according to the visibility map, but only when we can skip * at least SKIP_PAGES_THRESHOLD consecutive pages. Since we're reading * sequentially, the OS should be doing readahead for us, so there's no * gain in skipping a page now and then; that's likely to disable * readahead and so be counterproductive. Also, skipping even a single * page means that we can't update relfrozenxid, so we only want to do it * if we can skip a goodly number of pages. * unless aggressive is set, we want to skip all visible pages determined by vm, * but only if we can skip at least SKIP_PAGES_THRESHOLD consecutive pages. * because we read in order, the operating system should read it for us in advance, * so it's no good to skip a page from time to time; this may disable readahead, which is counterproductive. * and even skipping one page means we can't update relfrozenxid, so we just want to skip a considerable number of pages. * * When aggressive is set, we can't skip pages just because they are * all-visible, but we can still skip pages that are all-frozen, since * such pages do not need freezing and do not affect the value that we can * safely set for relfrozenxid or relminmxid. * when aggressive (T) is set, we can't skip pages just because they are visible, * but we can still skip all frozen pages because they don't need to be frozen, * and it doesn't affect that we can safely set new values for relfrozenxid or relminmxid. * * Before entering the main loop, establish the invariant that * next_unskippable_block is the next block number > = blkno that we can't * skip based on the visibility map, either all-visible for a regular scan * or all-frozen for an aggressive scan. We set it to nblocks if there's * no such block. We also set up the skipping_blocks flag correctly at * this stage. * create an invariant, that is, next_unskippable_block: the next block number > = blkno, before entering the main loop, * then we cannot skip it based on vm, which is fully visible for regular scans and completely frozen for active scans. * if such a block does not exist, then let's set it to nblocks. * at the same time, we have set the skipping_blocks flag correctly at this stage. * * Note: The value returned by visibilitymap_get_status could be slightly * out-of-date, since we make this test before reading the corresponding * heap page or locking the buffer. This is OK. If we mistakenly think * that the page is all-visible or all-frozen when in fact the flag's just * been cleared, we might fail to vacuum the page. It's easy to see that * skipping a page when aggressive is not set is not a very big deal; we * might leave some dead tuples lying around, but the next vacuum will * find them. But even when aggressive * is* set, it's still OK if we miss * a page whose all-frozen marking has just been cleared. Any new XIDs * just added to that page are necessarily newer than the GlobalXmin we * computed, so they'll have no effect on the value to which we can safely * set relfrozenxid. A similar argument applies for MXIDs and relminmxid. * Note: the value returned by visibilitymap_get_status may be a bit out of date, * because we tested it before reading the corresponding heap page or locking the buffer. There's nothing wrong with that. * if we mistakenly think that the page is all visible or frozen, * when we have just cleared the flag, then we may not be able to execute vacuum. * obviously, skipping a page without aggressive is a big problem; * We may leave some DEAD tuples, but the next vacuum will find them. * however, even if aggressive is set, it doesn't matter if we miss the page that has just cleared all the frozen tags. * any new xid just added to the page must be newer than the GlobalXmin we calculated, * so they do not affect our safe setting of the relfrozenxid value. * A similar view applies to mxid and relminmxid. * * We will scan the table's last page, at least to the extent of * determining whether it has tuples or not, even if it should be skipped * according to the above rules; except when we've already determined that * it's not worth trying to truncate the table. This avoids having * lazy_truncate_heap () take access-exclusive lock on the table to attempt * a truncation that just fails immediately because there are tuples in * the last page. This is worth avoiding mainly because such a lock must * be replayed on any hot standby, where it can be disruptive. * even if pages should be skipped according to the above rules, we will scan the last page of the table, * at least to the extent that can determine whether the table has tuples to determine if there are tuples, * unless we have determined that it is not worth trying to truncate the table, then we do not need to perform such a scan. * this prevents the lazy_truncate_heap () function from accessing the table exclusively and trying to truncate immediately because there are tuples in the last page. * this is worth it, mainly because such a lock must be replay on all hot standby, because it can cause damage. * / / the next unskipped block next_unskippable_block = 0; if ((options & VACOPT_DISABLE_PAGE_SKIPPING) = = 0) {/ / option does not disable skipping PAGE while (next_unskippable_block)

< nblocks)//循环k { uint8 vmstatus;//vm状态 vmstatus = visibilitymap_get_status(onerel, next_unskippable_block, &vmbuffer); if (aggressive) { if ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0) break;//遇到全冻结的block,跳出循环 } else { if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0) break;//如非强制扫描,遇到全可见block,跳出循环 } vacuum_delay_point(); next_unskippable_block++; } } if (next_unskippable_block >

= SKIP_PAGES_THRESHOLD) skipping_blocks = true;// is greater than the threshold, then set to T else skipping_blocks = false;// otherwise F for (blkno = 0; blkno)

< nblocks; blkno++) { //循环处理每个block Buffer buf;//缓冲区编号 Page page;//page OffsetNumber offnum,//偏移 maxoff; bool tupgone, hastup; int prev_dead_count;//上次已废弃元组统计 int nfrozen;//冻结统计 Size freespace;//空闲空间 bool all_visible_according_to_vm = false;//通过vm判断可见性的标记 bool all_visible;//全可见? bool all_frozen = true; /* provided all_visible is also true */ bool has_dead_tuples;//是否存在dead元组? TransactionId visibility_cutoff_xid = InvalidTransactionId;//事务ID /* see note above about forcing scanning of last page */ //请查看上述关于最后一个page的强制扫描注释 //全部扫描&尝试截断#define FORCE_CHECK_PAGE() \ (blkno == nblocks - 1 && should_attempt_truncation(vacrelstats)) //更新统计信息 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno); if (blkno == next_unskippable_block) { //到达了next_unskippable_block标记的地方 /* Time to advance next_unskippable_block */ //是时候增加next_unskippable_block计数了 next_unskippable_block++; //寻找下一个需跳过的block if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0) { while (next_unskippable_block < nblocks) { uint8 vmskipflags; vmskipflags = visibilitymap_get_status(onerel, next_unskippable_block, &vmbuffer); if (aggressive) { if ((vmskipflags & VISIBILITYMAP_ALL_FROZEN) == 0) break; } else { if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0) break; } vacuum_delay_point(); next_unskippable_block++; } } /* * We know we can't skip the current block. But set up * skipping_blocks to do the right thing at the following blocks. * 不能跳过当前block. * 但设置skipping_blocks标记处理接下来的blocks */ if (next_unskippable_block - blkno >

SKIP_PAGES_THRESHOLD) skipping_blocks = true; else skipping_blocks = false; / * * Normally, the fact that we can't skip this block must mean that * it's not all-visible. But in an aggressive vacuum we know only * that it's not all-frozen, so it might still be all-visible. * usually, the fact that we can't skip this block must mean that it is not completely visible. * but in an aggressive vacuum, all we know is that it is not completely frozen, so it may still be completely visible. * / if (aggressive & & VM_ALL_VISIBLE (onerel, blkno, & vmbuffer)) all_visible_according_to_vm = true;} else {/ / has not yet reached the next_unskippable_block mark / * * The current block is potentially skippable If we've seen a * long enough run of skippable blocks to justify skipping it, and * we're not forced to check it, then go ahead and skip. * Otherwise, the page must be at least all-visible if not * all-frozen, so we can set all_visible_according_to_vm = true. * the current block may be skippable; if we have seen enough skippable block run time, we can skip it, * and if we do not need to check, then continue to skip it. * otherwise, the page must be at least visible (if not all frozen), * so we can set all_visible_according_to_vm = true. * / if (skipping_blocks & &! FORCE_CHECK_PAGE ()) {/ * * Tricky, tricky. If this is in aggressive vacuum, the page * must have been all-frozen at the time we checked whether it * was skippable, but it might not be any more. We must be * careful to count it as a skipped all-frozen page in that * case, or else we'll think we can't update relfrozenxid and * relminmxid. If it's not an aggressive vacuum, we don't * know whether it was all-frozen, so we have to recheck; but * in this case an approximate answer is OK. * difficult, difficult. If this is in aggressive vacuum, * then by the time we check to see if the page can be skipped, the page must have been completely frozen, but it probably won't now. * in this case, we must be careful to think of it as all frozen pages skipped, * otherwise we will consider it impossible to update relfrozenxid and relminmxid. * if it's not an aggressive vacuum, we don't know if it's completely frozen, * so we have to re-check; but in this case, the approximate answer is yes. * / if (aggressive | | VM_ALL_FROZEN (onerel, blkno, & vmbuffer) vacrelstats- > page count completely frozen by frozenskipped_pages++;// + 1 continue;// jumps to the next block} all_visible_according_to_vm = true;} vacuum_delay_point () / * * If we are close to overrunning the available space for dead-tuple * TIDs, pause and do a cycle of vacuuming before we tackle this page. * if you are about to run out of free space in the DEAD tuple tid, pause and execute a vacuuming loop before processing this page. * / if ((vacrelstats- > max_dead_tuples-vacrelstats- > num_dead_tuples)

< MaxHeapTuplesPerPage && vacrelstats->

Num_dead_tuples > 0) {/ / there are obsolete tuples, and / / MaxHeapTuplesPerPage + vacrelstats- > num_dead_tuples > vacrelstats- > max_dead_tuples const int hvp_index [] = {PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_NUM_INDEX_VACUUMS}; int64 hvp_val [2] / * Before beginning index vacuuming, we release any pin we may * hold on the visibility map page. This isn't necessary for * correctness, but we do it anyway to avoid holding the pin * across a lengthy, unrelated operation. * release all pin held on vm page before starting index vacuuming. * this is not necessary for correctness, but we do so to avoid holding pin in a lengthy, irrelevant operation. * / if (BufferIsValid (vmbuffer)) {ReleaseBuffer (vmbuffer); vmbuffer = InvalidBuffer;} / * Log cleanup info before we touch indexes * / clear the log information vacuum_log_cleanup_info (onerel, vacrelstats) before starting processing indexes / * Report that we are now vacuuming indexes * / / cleaning vacuum indexes pgstat_progress_update_param (PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_VACUUM_INDEX) / * Remove index entries * / / traverses index relation, executes vacuum / / deletes index entries pointing to vacrelstats- > dead_tuples tuple, and updates runtime statistics for (I = 0; I

< nindexes; i++) lazy_vacuum_index(Irel[i], &indstats[i], vacrelstats); /* * Report that we are now vacuuming the heap. We also increase * the number of index scans here; note that by using * pgstat_progress_update_multi_param we can update both * parameters atomically. * 报告正在vacumming heap. * 这里会增加索引扫描,注意通过设置pgstat_progress_update_multi_param参数可以同时自动更新参数. */ hvp_val[0] = PROGRESS_VACUUM_PHASE_VACUUM_HEAP; hvp_val[1] = vacrelstats->

< nindexes; i++) lazy_vacuum_index(Irel[i], &indstats[i], vacrelstats); /* Report that we are now vacuuming the heap */ //报告我们正在vacuuming heap hvp_val[0] = PROGRESS_VACUUM_PHASE_VACUUM_HEAP; hvp_val[1] = vacrelstats->

Num_index_scans+ 1; pgstat_progress_update_multi_param (2, hvp_index, hvp_val); / * Remove tuples from heap * / / Clean tuple pgstat_progress_update_param (PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_VACUUM_HEAP); lazy_vacuum_heap (onerel, vacrelstats); vacrelstats- > num_index_scans++ } / * Vacuum the remainder of the Free Space Map. We must do this whether or * not there were indexes. * vacuum FSM. * this must be done regardless of whether an index exists or not. * / if (blkno > next_fsm_block_to_vacuum) FreeSpaceMapVacuumRange (onerel, next_fsm_block_to_vacuum, blkno); / * report all blocks vacuumed; and that we're cleaning up * / / report all blocks vacuumed completed. Pgstat_progress_update_param (PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno); pgstat_progress_update_param (PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE_INDEX_CLEANUP); / * Do post-vacuum cleanup and statistics update for each index * / / perform the vacuum wrap-up, updating statistics for (I = 0; I) for each index

< nindexes; i++) lazy_cleanup_index(Irel[i], indstats[i], vacrelstats); /* If no indexes, make log report that lazy_vacuum_heap would've made */ //如无索引,写日志 if (vacuumed_pages) ereport(elevel, (errmsg("\"%s\": removed %.0f row versions in %u pages", RelationGetRelationName(onerel), tups_vacuumed, vacuumed_pages))); /* * 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); appendStringInfo(&buf, _("%.0f dead row versions cannot be removed yet, oldest xmin: %u\n"), nkeep, OldestXmin); appendStringInfo(&buf, _("There were %.0f unused item pointers.\n"), nunused); appendStringInfo(&buf, ngettext("Skipped %u page due to buffer pins, ", "Skipped %u pages due to buffer pins, ", vacrelstats->

Pinskipped_pages), vacrelstats- > pinskipped_pages); appendStringInfo (& buf, ngettext ("% u frozen page.\ n", "% u frozen pages.\ n", vacrelstats- > frozenskipped_pages), vacrelstats- > frozenskipped_pages) AppendStringInfo (& buf, ngettext ("% u page is entirely empty.\ n", "% u pages are entirely empty.\ n", empty_pages), empty_pages); appendStringInfo (& buf, _ ("% s."), pg_rusage_show (& ru0)) Ereport (elevel, (errmsg ("\"% s\ ": found% .0f removable,% .0f nonremovable row versions in% u out of% u pages", RelationGetRelationName (onerel), tups_vacuumed, num_tuples, vacrelstats- > scanned_pages, nblocks), errdetail_internal ("% s", buf.data)); pfree (buf.data) Third, follow-up and analysis

Test script, execute vacuum while performing stress test

-- session 1pgbench-c 2-C-f. / update.sql-j 1-n-T 600-U xdb testdb-- session 217 xdb@ 52 xdb@ (local]: 5432) testdb=# vacuum verbose T1

Start gdb and set breakpoint

(gdb) b lazy_scan_heapBreakpoint 1 at 0x6bc38a: file vacuumlazy.c, line 470. (gdb) cContinuing.Breakpoint 1, lazy_scan_heap (onerel=0x7f224a197788, options=5, vacrelstats=0x296d7b8, Irel=0x296d8b0, nindexes=1, aggressive=false) at vacuumlazy.c:470470 TransactionId relfrozenxid = onerel- > rd_rel- > relfrozenxid; (gdb)

Input parameters

1-relation

(gdb) p * onerel$1 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50820}, rd_smgr = 0x2930270, rd_refcnt = 1, rd_backend =-1, rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 1'\ 001, rd_statvalid = false, rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7f224a197bb8, rd_att = 0x7f224a0d8050, rd_id = 50820, rd_lockInfo = {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 = 0x7f224a198fe8, rd_oidindex = 0, rd_pkindex = 0, rd_replidindex = 0, rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, rd_pkattr = rd_pkattr, 0x0 = 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 = rd_amcache, 0x0 = 0x0, 0x0 = rd_indcollation Rd_toastoid = 0, pgstat_info = 0x2923e50} (gdb)

2-options=5, i.e. VACOPT_VACUUM | VACOPT_VERBOSE

3-vacrelstats

(gdb) p * vacrelstats$2 = {hasindex = true, 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)

4-Irel

(gdb) p * Irel$3 = (Relation) 0x7f224a198688 (gdb) p * * Irel$4 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50823}, rd_smgr = 0x29302e0, rd_refcnt = 1, rd_backend =-1, rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 0000, rd_statvalid = false, rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7f224a1988a0, rd_att = 0x7f224a1989b8, rd_id = 50823 Rd_lockInfo = {lockRelId = {relId = 50823, 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 = rd_projindexattr Rd_keyattr = 0x0, rd_pkattr = 0x0, rd_idattr = 0x0, rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x7f224a198d58, rd_indextuple = 0x7f224a198d20, rd_amhandler = 330,330,rd_indexcxt = 0x28cb340, rd_amroutine = 0x28cb480, rd_opfamily = 0x28cb598, rd_opcintype = 0x28cb5b8, rd_support = 0x28cb5d8, rd_supportinfo = 0x28cb600, rd_indoption = 0x28cb738, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = rd_exclops, 0x0 = 0x0, 0x0 = rd_exclprocs Rd_amcache = 0x0, rd_indcollation = 0x28cb718, rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x2923ec8} (gdb)

5-nindexes=1, there is an index

6-aggressive=false, no need to perform a full table scan

Let's initialize the relevant variables

(gdb) n471 TransactionId relminmxid = onerel- > rd_rel- > relminmxid; (gdb) 483 Buffer vmbuffer = InvalidBuffer; (gdb) 488 const int initprog_index [] = {(gdb) 495 pg_rusage_init (& ru0); (gdb) 497 relname = RelationGetRelationName (onerel); (gdb) 498 if (aggressive) (gdb) 504 ereport (elevel, (gdb) 509 empty_pages = vacuumed_pages = 0 (gdb) 510 next_fsm_block_to_vacuum = (BlockNumber) 0; (gdb) 511 num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = 0; (gdb) 514 palloc0 (nindexes * sizeof (IndexBulkDeleteResult *)); (gdb) 513 indstats = (IndexBulkDeleteResult *) (gdb) 516 nblocks = RelationGetNumberOfBlocks (onerel) (gdb) p relminmxid$5 = 1 (gdb) p ru0 $6 = {tv = {tv_sec = 1548669429, tv_usec = 578779}, ru = {ru_utime = {tv_sec = 0, tv_usec = 29531}, ru_stime = {tv_sec = 0, tv_usec = 51407}, {ru_maxrss = 7488, _ _ ru_maxrss_word = 7488}, {ru_ixrss = 0, _ ru_ixrss_word = 0}, {ru_idrss = 0 _ _ ru_idrss_word = 0}, {ru_isrss = 0, _ _ ru_isrss_word = 0}, {ru_minflt = 1819, _ _ ru_minflt_word = 1819}, {ru_majflt = 0, _ _ ru_majflt_word = 0}, {ru_nswap = 0, _ _ ru_nswap_word = 0}, {ru_inblock = 2664, _ _ ru_inblock_word = 2664}, {ru_oublock = 328 _ _ ru_oublock_word = 328}, {ru_msgsnd = 0, _ _ ru_msgsnd_word = 0}, {ru_msgrcv = 0, _ _ ru_msgrcv_word = 0}, {ru_nsignals = 0, _ _ ru_nsignals_word = 0}, {ru_nvcsw = 70, _ _ ru_nvcsw_word = 70}, {ru_nivcsw = 3, _ _ ru_nivcsw_word = 3}} (gdb) p relname$7 = 0x7f224a197bb8 "T1" (gdb)

Get the total number of blocks

(gdb) n517 vacrelstats- > rel_pages = nblocks; (gdb) p nblocks$8 = 75 (gdb)

Initialize statistics and related arrays

(gdb) n518 vacrelstats- > scanned_pages = 0; (gdb) 519 vacrelstats- > tupcount_pages = 0; (gdb) 520 vacrelstats- > nonempty_pages = 0; (gdb) 521 vacrelstats- > latestRemovedXid = InvalidTransactionId; (gdb) 523 lazy_space_alloc (vacrelstats, nblocks); (gdb) 524 frozen = palloc (sizeof (xl_heap_freeze_tuple) * MaxHeapTuplesPerPage); (gdb) 527 initprog_val [0] = PROGRESS_VACUUM_PHASE_SCAN_HEAP (gdb) 528 initprog_val [1] = nblocks; (gdb) 529 initprog_val [2] = vacrelstats- > max_dead_tuples; (gdb) 530 pgstat_progress_update_multi_param (3, initprog_index, initprog_val) (gdb) p * vacrelstats$9 = {hasindex = true, old_rel_pages = 75, rel_pages = 75, 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 = 21825, dead_tuples = 0x297e820 Num_index_scans = 0, latestRemovedXid = 0, lock_waiter_detected = false} (gdb)

Calculate the next block that cannot be skipped

The 0th block cannot be skipped (0

< 32),设置标记skipping_blocks为F (gdb) n576 next_unskippable_block = 0;(gdb) 577 if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)(gdb) 579 while (next_unskippable_block < nblocks)(gdb) 583 vmstatus = visibilitymap_get_status(onerel, next_unskippable_block,(gdb) 585 if (aggressive)(gdb) p vmstatus$10 = 0 '\000'(gdb) n592 if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0)(gdb) 593 break;(gdb) 600 if (next_unskippable_block >

= SKIP_PAGES_THRESHOLD) (gdb) p next_unskippable_block$11 = 0 (gdb) p SKIP_PAGES_THRESHOLD$12 = 32 (gdb) n603 skipping_blocks = false; (gdb)

Start traversing each block

Initialize related variables

(gdb) 605 for (blkno = 0; blkno)

< nblocks; blkno++)(gdb) 616 bool all_visible_according_to_vm = false;(gdb) 618 bool all_frozen = true; /* provided all_visible is also true */(gdb) 620 TransactionId visibility_cutoff_xid = InvalidTransactionId;(gdb) 626 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);(gdb) 628 if (blkno == next_unskippable_block)(gdb) blkno == next_unskippable_block,获取下一个不可跳过的block (gdb) p blkno$13 = 0(gdb) p next_unskippable_block$14 = 0(gdb) n631 next_unskippable_block++;(gdb) 632 if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)(gdb) 634 while (next_unskippable_block < nblocks)(gdb) 638 vmskipflags = visibilitymap_get_status(onerel,(gdb) 641 if (aggressive)(gdb) p vmskipflags$15 = 0 '\000'(gdb) n648 if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0)(gdb) 649 break;(gdb) 660 if (next_unskippable_block - blkno >

SKIP_PAGES_THRESHOLD) (gdb) p next_unskippable_block$16 = 1 (gdb) n 1047 if (onerel- > rd_rel- > relhasoids & & (gdb) 1132 if (tupgone) (gdb)

Tupgone is F to determine whether it needs to be frozen (F)

Get offset, traverse tuple

(gdb) p tupgone$17 = false (gdb) n1144 num_tuples + = 1; (gdb) 1145 hastup = true (gdb) 1151 if (heap_prepare_freeze_tuple (tuple.t_data, (gdb) 1154 & frozen [nfrozen], (gdb) p nfrozen$18 = 0 (gdb) n1151 if (heap_prepare_freeze_tuple (tuple.t_data) (gdb) 1158 if (! tuple_totally_frozen) (gdb) 1159 all_frozen = false (gdb) 958 offnum = OffsetNumberNext (offnum)) (gdb) 956 for (offnum = FirstOffsetNumber; (gdb)

The tuple is normal

(gdb) p offnum$19 = 3 (gdb) n962 itemid = PageGetItemId (page, offnum); (gdb) 965 if (! ItemIdIsUsed (itemid)) (gdb) 972 if (ItemIdIsRedirected (itemid)) (gdb) 978 ItemPointerSet (& (tuple.t_self), blkno, offnum); (gdb) 986 if (ItemIdIsDead (itemid)) (gdb) 993 Assert (ItemIdIsNormal (itemid)) (gdb) 995 tuple.t_data = (HeapTupleHeader) PageGetItem (page, itemid); (gdb) 996 tuple.t_len = ItemIdGetLength (itemid); (gdb) 997 tuple.t_tableOid = RelationGetRelid (onerel); (gdb) 999 tupgone = false; (gdb)

Call HeapTupleSatisfiesVacuum to determine the tuple state, the main purpose is whether a tuple can be visible to all running transactions

The tuple is Live tuple

1012 switch (& tuple, OldestXmin, buf) (gdb) (gdb) n1047 if (onerel- > rd_rel- > relhasoids & & (gdb) n1056 live_tuples + = 1; (gdb) 1067 if (all_visible) (gdb) p all_visible$20 = false

Jump out of the cycle

(gdb) b vacuumlazy.c:1168Breakpoint 2 at 0x6bd4e7: file vacuumlazy.c, line 1168. (gdb) cContinuing.Breakpoint 2, lazy_scan_heap (onerel=0x7f224a197788, options=5, vacrelstats=0x296d7b8, Irel=0x296d8b0, nindexes=1, aggressive=false) at vacuumlazy.c:11681168 if (nfrozen > 0) (gdb)

Update statistics

(gdb) n1203 if (nindexes = = 0 & & (gdb) p nfrozen$23 = 0 (gdb) n1232 freespace = PageGetHeapFreeSpace (page) (gdb) 1235 if (all_visible & &! all_visible_according_to_vm) (gdb) 1268 else if (all_visible_according_to_vm & & PageIsAllVisible (page) (gdb) 1290 else if (PageIsAllVisible (page) & & has_dead_tuples) (gdb) 1305 else if (all_visible_according_to_vm & all_visible & & all_frozen & & (gdb) 1318 UnlockReleaseBuffer (buf)) (gdb) 1321 if (hastup) (gdb) 1322 vacrelstats- > nonempty_pages = blkno + 1; (gdb) p hastup$24 = true (gdb) n1331 if (vacrelstats- > num_dead_tuples = = prev_dead_count) (gdb) 1332 RecordPageWithFreeSpace (onerel, blkno, freespace)

Move on to the next block

(gdb) 605 for (blkno = 0; blkno)

< nblocks; blkno++)(gdb) p blkno$25 = 0(gdb) n616 bool all_visible_according_to_vm = false;(gdb) p blkno$26 = 1(gdb) 判断(vacrelstats->

Max_dead_tuples-vacrelstats- > num_dead_tuples)

< MaxHeapTuplesPerPage && vacrelstats->

Num_dead_tuples > 0. If not satisfied, continue execution.

(gdb) 701 vacuum_delay_point (); (gdb) 707 if ((vacrelstats- > max_dead_tuples-vacrelstats- > num_dead_tuples)

< MaxHeapTuplesPerPage &&(gdb) p vacrelstats->

Max_dead_tuples$27 = 21825 (gdb) p vacrelstats- > num_dead_tuples$28 = 0 (gdb) p MaxHeapTuplesPerPageNo symbol "_ builtin_offsetof" in current context. (gdb)

Read buffer in an extended way

(gdb) n783 visibilitymap_pin (onerel, blkno, & vmbuffer); (gdb) 785 buf = ReadBufferExtended (onerel, MAIN_FORKNUM, blkno, (gdb) 789 if (! ConditionalLockBufferForCleanup (buf)) (gdb)

Get buffer cleanup lock, success!

Call heap_page_prune to clean up all HOT-update chains in the page

(gdb) n847 vacrelstats- > scanned_pages++; (gdb) 848 vacrelstats- > tupcount_pages++; (gdb) 850 page = BufferGetPage (buf); (gdb) 852 if (PageIsNew (page)) (gdb) 894 if (PageIsEmpty (page)) (gdb) 938 tups_vacuumed + = heap_page_prune (onerel, buf, OldestXmin, false, (gdb) 945 all_visible = true; (gdb)

Traversing row pointers in page

956 for (offnum = FirstOffsetNumber; (gdb) p maxoff$29 = 291 (gdb) $30 (gdb) n962 itemid = PageGetItemId (page, offnum); (gdb) n965 if (! ItemIdIsUsed (itemid)) (gdb) 972 if (ItemIdIsRedirected (itemid)) (gdb) 978 ItemPointerSet (& (tuple.t_self), blkno, offnum) (gdb) 986 if (ItemIdIsDead (itemid)) (gdb) 993 Assert (ItemIdIsNormal (itemid)); (gdb) 995 tuple.t_data = (HeapTupleHeader) PageGetItem (page, itemid); (gdb) 996 tuple.t_len = ItemIdGetLength (itemid); (gdb) 997 tuple.t_tableOid = RelationGetRelid (onerel); (gdb) 999 tupgone = false (gdb) 1012 switch (& tuple, OldestXmin, buf) (gdb) 1099 nkeep + = 1; (gdb) 1100 all_visible = false; (gdb) 1101 break; (gdb) 1132 if (tupgone) (gdb) 1144 num_tuples + = 1

Jump out of the cycle

(gdb) cContinuing.Breakpoint 2, lazy_scan_heap (onerel=0x7f224a197788, options=5, vacrelstats=0x296d7b8, Irel=0x296d8b0, nindexes=1, aggressive=false) at vacuumlazy.c:11681168 if (nfrozen > 0) (gdb)

DONE!

IV. Reference materials

PG Source Code

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 295

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report