您好,登錄后才能下訂單哦!
這篇文章主要介紹“PostgreSQL的vacuum過程中lazy_vacuum_heap函數有什么作用”,在日常操作中,相信很多人在PostgreSQL的vacuum過程中lazy_vacuum_heap函數有什么作用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”PostgreSQL的vacuum過程中lazy_vacuum_heap函數有什么作用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
本節簡單介紹了PostgreSQL手工執行vacuum的處理流程,主要分析了ExecVacuum->vacuum->vacuum_rel->heap_vacuum_rel->lazy_scan_heap->lazy_vacuum_heap函數的實現邏輯,該函數訪問堆表,標記廢棄元組為未使用并在這些元組所在頁面上壓縮空閑空間。
宏定義
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;
itemIdSort
PageRepairFragmentation/PageIndexMultiDelete的排序支持
/* * sorting support for PageRepairFragmentation and PageIndexMultiDelete * PageRepairFragmentation/PageIndexMultiDelete的排序支持 */ typedef struct itemIdSortData { //行指針數組索引 uint16 offsetindex; /* linp array index */ //item數據頁內偏移 int16 itemoff; /* page offset of item data */ //對齊長度 uint16 alignedlen; /* MAXALIGN(item data len) */ } itemIdSortData; //結構體指針 typedef itemIdSortData *itemIdSort;
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 */ //ItemPointer數組 ItemPointer dead_tuples; /* array of ItemPointerData */ //掃描的索引數 int num_index_scans; //最后被清除的事務ID TransactionId latestRemovedXid; //是否存在waiter? bool lock_waiter_detected; } LVRelStats;
ItemPointer
行指針
typedef struct ItemPointerData { BlockIdData ip_blkid;//塊號 OffsetNumber ip_posid;//塊內偏移 } typedef ItemPointerData *ItemPointer;
lazy_vacuum_heap
lazy_vacuum_heap標記廢棄元組為未使用并在這些元組所在頁面上壓縮空閑空間,在此期間,不會訪問lazy_scan_heap標記為存活元組的頁面.
主要處理流程如下:
1.初始化變量
2.遍歷vacrelstats->num_dead_tuples行指針數組(ItemPointer)
2.1獲取塊號/讀取塊到緩沖區中
2.2加鎖,如不成功,則處理下一個元組
2.3調用lazy_vacuum_page釋放空間,整理碎片
2.4獲取page,獲取該page的空閑空間
2.5釋放緩沖,記錄空閑空間
3.收尾工作
/* * lazy_vacuum_heap() -- second pass over the heap * lazy_vacuum_heap() -- 二次訪問堆表 * * This routine marks dead tuples as unused and compacts out free * space on their pages. Pages not having dead tuples recorded from * lazy_scan_heap are not visited at all. * lazy_vacuum_heap標記廢棄元組為未使用并在這些元組所在頁面上壓縮空閑空間. * 在此期間,不會訪問lazy_scan_heap標記沒有廢棄元組的頁面. * * Note: the reason for doing this as a second pass is we cannot remove * the tuples until we've removed their index entries, and we want to * process index entry removal in batches as large as possible. * 注意:二次訪問堆表的原因是在清除索引條目前不能清除元組, * 而且我們希望以批量的方式處理索引條目,越大越好. */ static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats) { int tupindex;//元組索引 int npages;//頁面數 PGRUsage ru0; Buffer vmbuffer = InvalidBuffer;//vm緩沖 pg_rusage_init(&ru0);//初始化 npages = 0; tupindex = 0; //遍歷廢棄元組 //vacrelstats->dead_tuples數組中的元素類型ItemPointer while (tupindex < vacrelstats->num_dead_tuples) { BlockNumber tblk;//塊號 Buffer buf;//緩沖 Page page;//頁面 Size freespace; vacuum_delay_point(); //獲取塊號 tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]); //以擴展方式讀取buffer buf = ReadBufferExtended(onerel, MAIN_FORKNUM, tblk, RBM_NORMAL, vac_strategy); //獲取鎖(不等待) if (!ConditionalLockBufferForCleanup(buf)) { //獲取不了,釋放資源,跳轉到下一個元組 ReleaseBuffer(buf); ++tupindex; continue; } //釋放page中的廢棄元組,并整理碎片 tupindex = lazy_vacuum_page(onerel, tblk, buf, tupindex, vacrelstats, &vmbuffer); /* Now that we've compacted the page, record its available space */ //現在已經壓縮了頁面(釋放了空間),記錄可用空間 page = BufferGetPage(buf); freespace = PageGetHeapFreeSpace(page); UnlockReleaseBuffer(buf); RecordPageWithFreeSpace(onerel, tblk, freespace); npages++; } if (BufferIsValid(vmbuffer)) { //釋放緩沖區 ReleaseBuffer(vmbuffer); vmbuffer = InvalidBuffer; } ereport(elevel, (errmsg("\"%s\": removed %d row versions in %d pages", RelationGetRelationName(onerel), tupindex, npages), errdetail_internal("%s", pg_rusage_show(&ru0)))); }
lazy_vacuum_page
lazy_vacuum_page釋放page中的廢棄元組,并整理碎片
主要處理邏輯如下:
1.初始化相關變量
2.遍歷廢棄元組數組
2.1獲取塊號,如塊號不一致,跳出循環
2.2獲取偏移/行指針
2.3標記為未使用,記錄偏移
3.調用PageRepairFragmentation整理碎片
3.1判斷和檢查(嚴謹的編碼!!!)
3.2獲取偏移,初始化變量
3.3遍歷行指針數組
3.3.1獲取行指針lp
3.3.2如ItemId正在使用,記錄到itemidbase數組中;否則標記ItemId未被使用
3.4計算數組中存儲的元素個數
A.如個數為0,重置page
B.否則調用compactify_tuples壓縮頁
3.5為PageAddItem方法設置標記位
4.標記buffer為dirty
5.寫入WAL Record
6.如all-visible,則設置頁面all-visible標記
7.如page為all-visible,設置vm
8.返回下一個page的起始數組編號
/* * lazy_vacuum_page() -- free dead tuples on a page * and repair its fragmentation. * lazy_vacuum_page() -- 釋放page中的廢棄元組,并整理碎片 * * Caller must hold pin and buffer cleanup lock on the buffer. * 調用者必須持有buffer的pin和cleanup鎖才能執行 * * tupindex is the index in vacrelstats->dead_tuples of the first dead * tuple for this page. We assume the rest follow sequentially. * The return value is the first tupindex after the tuples of this page. * tupindex是該page中第一個廢棄元組在vacrelstats->dead_tuples中的編號,我們假定余下元組是順序的. * 返回值是該page中的元組后的第一個編號tupindex. */ static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, int tupindex, LVRelStats *vacrelstats, Buffer *vmbuffer) { //獲取page Page page = BufferGetPage(buffer); OffsetNumber unused[MaxOffsetNumber];//偏移數組 int uncnt = 0; TransactionId visibility_cutoff_xid;//事務ID bool all_frozen;//釋放全部凍結 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno); //進入關鍵處理部分 START_CRIT_SECTION(); //遍歷廢棄元組數組 for (; tupindex < vacrelstats->num_dead_tuples; tupindex++) { BlockNumber tblk;//塊號 OffsetNumber toff;//偏移 ItemId itemid;//行指針 //根據行指針獲取塊號 tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]); if (tblk != blkno) //不是同一個塊,跳出循環 break; /* past end of tuples for this block */ //獲取偏移 toff = ItemPointerGetOffsetNumber(&vacrelstats->dead_tuples[tupindex]); //獲取行指針 itemid = PageGetItemId(page, toff); //標記為未使用 ItemIdSetUnused(itemid); //記錄偏移 unused[uncnt++] = toff; } //整理碎片 PageRepairFragmentation(page); /* * Mark buffer dirty before we write WAL. * 標記buffer為dirty */ MarkBufferDirty(buffer); /* XLOG stuff */ if (RelationNeedsWAL(onerel)) { //記錄WAL Record XLogRecPtr recptr; recptr = log_heap_clean(onerel, buffer, NULL, 0, NULL, 0, unused, uncnt, vacrelstats->latestRemovedXid); PageSetLSN(page, recptr); } /* * End critical section, so we safely can do visibility tests (which * possibly need to perform IO and allocate memory!). If we crash now the * page (including the corresponding vm bit) might not be marked all * visible, but that's fine. A later vacuum will fix that. * 結束關鍵區域,這樣我們可以安全的執行可見性檢查 * (這可能需要執行IO/分配內存) * 如果進程崩潰,頁面(包括相應的vm位)可能標記為all-visible,但這也沒有問題,后續vacuum會修復. */ END_CRIT_SECTION(); /* * Now that we have removed the dead tuples from the page, once again * check if the page has become all-visible. The page is already marked * dirty, exclusively locked, and, if needed, a full page image has been * emitted in the log_heap_clean() above. * 現在,我們已經從頁面中刪除了廢棄的元組,再次檢查頁面是否已經全部可見。 * 頁面已經被標記為dirty、獨占鎖定,如需要,還會在log_heap_clean()中記錄完整的頁面鏡像。 */ if (heap_page_is_all_visible(onerel, buffer, &visibility_cutoff_xid, &all_frozen)) PageSetAllVisible(page); /* * All the changes to the heap page have been done. If the all-visible * flag is now set, also set the VM all-visible bit (and, if possible, the * all-frozen bit) unless this has already been done previously. * 堆頁面的所有修改已完成.如果設置了all-visible標記,同時設置VM all-visible位 * (而且,如可能,設置all-frozen位),除非先前已完成. */ if (PageIsAllVisible(page)) { uint8 vm_status = visibilitymap_get_status(onerel, blkno, vmbuffer); uint8 flags = 0; /* Set the VM all-frozen bit to flag, if needed */ //如需要,設置VM all-frozen標記位 if ((vm_status & VISIBILITYMAP_ALL_VISIBLE) == 0) flags |= VISIBILITYMAP_ALL_VISIBLE; if ((vm_status & VISIBILITYMAP_ALL_FROZEN) == 0 && all_frozen) flags |= VISIBILITYMAP_ALL_FROZEN; Assert(BufferIsValid(*vmbuffer)); if (flags != 0) visibilitymap_set(onerel, blkno, buffer, InvalidXLogRecPtr, *vmbuffer, visibility_cutoff_xid, flags); } return tupindex; } /* * PageRepairFragmentation * * Frees fragmented space on a page. * 釋放頁面上的碎片空間. * * It doesn't remove unused line pointers! Please don't change this. * 該方法不會清楚未使用的行指針!因此,不要修改它. * * This routine is usable for heap pages only, but see PageIndexMultiDelete. * 該方法只用于堆頁面,但注意參考PageIndexMultiDelete. * * As a side effect, the page's PD_HAS_FREE_LINES hint bit is updated. * 該方法在處理的時候,頁面的PD_HAS_FREE_LINES標記位會被更新. * */ void PageRepairFragmentation(Page page) { Offset pd_lower = ((PageHeader) page)->pd_lower; Offset pd_upper = ((PageHeader) page)->pd_upper; Offset pd_special = ((PageHeader) page)->pd_special; itemIdSortData itemidbase[MaxHeapTuplesPerPage];//存儲數據 itemIdSort itemidptr; ItemId lp; int nline, nstorage, nunused; int i; Size totallen; /* * It's worth the trouble to be more paranoid here than in most places, * because we are about to reshuffle data in (what is usually) a shared * disk buffer. If we aren't careful then corrupted pointers, lengths, * etc could cause us to clobber adjacent disk buffers, spreading the data * loss further. So, check everything. * 在這里比在其他地方執行更多的檢查是值得的,因為我們將在(通常是)共享磁盤緩沖區中重新洗牌數據。 * 如果我們不小心,那么損壞的行指針、數據長度等可能會導致與相鄰磁盤緩沖區沖突, * 如果錯誤進一步傳播會導致數據丟失。因此,需要仔細檢查。 */ if (pd_lower < SizeOfPageHeaderData || pd_lower > pd_upper || pd_upper > pd_special || pd_special > BLCKSZ || pd_special != MAXALIGN(pd_special)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u", pd_lower, pd_upper, pd_special))); /* * Run through the line pointer array and collect data about live items. * 遍歷行指針數組,收集存活的條目. */ nline = PageGetMaxOffsetNumber(page);//獲取最大的偏移 itemidptr = itemidbase;// nunused = totallen = 0; for (i = FirstOffsetNumber; i <= nline; i++) { //---------- 遍歷行指針數組 //獲取line pointer lp = PageGetItemId(page, i); if (ItemIdIsUsed(lp)) { //如果ItemId在使用 if (ItemIdHasStorage(lp)) { //如ItemID與存儲相關,判斷條件:((itemId)->lp_len != 0) itemidptr->offsetindex = i - 1; itemidptr->itemoff = ItemIdGetOffset(lp); //執行判斷 if (unlikely(itemidptr->itemoff < (int) pd_upper || itemidptr->itemoff >= (int) pd_special)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item pointer: %u", itemidptr->itemoff))); //對齊長度 itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp)); totallen += itemidptr->alignedlen; itemidptr++;//數組下一個元素 } } else { /* Unused entries should have lp_len = 0, but make sure */ //未使用的ItemId ItemIdSetUnused(lp); nunused++; } } //數組中存儲的元素個數 nstorage = itemidptr - itemidbase; if (nstorage == 0) { /* Page is completely empty, so just reset it quickly */ //page完全是空的,重置page ((PageHeader) page)->pd_upper = pd_special; } else { /* Need to compact the page the hard way */ //page非空,壓縮頁 if (totallen > (Size) (pd_special - pd_lower)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("corrupted item lengths: total %u, available space %u", (unsigned int) totallen, pd_special - pd_lower))); compactify_tuples(itemidbase, nstorage, page); } /* Set hint bit for PageAddItem */ //為PageAddItem方法設置標記位 if (nunused > 0) //存在未使用的空位,設置標記 PageSetHasFreeLinePointers(page); else //清除標記 PageClearHasFreeLinePointers(page); } /* * After removing or marking some line pointers unused, move the tuples to * remove the gaps caused by the removed items. * 在清除或者標記某些行指針為沒有使用后,移動元組以消除已刪除元組之間的鴻溝 */ static void compactify_tuples(itemIdSort itemidbase, int nitems, Page page) { PageHeader phdr = (PageHeader) page; Offset upper; int i; /* sort itemIdSortData array into decreasing itemoff order */ //以itemoff降序的方式排序itemIdSortData數組 qsort((char *) itemidbase, nitems, sizeof(itemIdSortData), itemoffcompare); //重整page upper = phdr->pd_special; for (i = 0; i < nitems; i++) { itemIdSort itemidptr = &itemidbase[i]; ItemId lp; lp = PageGetItemId(page, itemidptr->offsetindex + 1); upper -= itemidptr->alignedlen; memmove((char *) page + upper, (char *) page + itemidptr->itemoff, itemidptr->alignedlen); lp->lp_off = upper; } phdr->pd_upper = upper; } /* * ItemIdSetUnused * Set the item identifier to be UNUSED, with no storage. * Beware of multiple evaluations of itemId! * 設置ItemId為未使用. */ #define ItemIdSetUnused(itemId) \ ( \ (itemId)->lp_flags = LP_UNUSED, \ (itemId)->lp_off = 0, \ (itemId)->lp_len = 0 \ )
測試腳本 : 刪除數據,執行vacuum
11:04:59 (xdb@[local]:5432)testdb=# delete from t1 where id < 600; DELETE 100 14:26:16 (xdb@[local]:5432)testdb=# checkpoint; CHECKPOINT 11:18:29 (xdb@[local]:5432)testdb=# vacuum verbose t1;
lazy_vacuum_heap
啟動gdb,設置斷點
(gdb) b lazy_vacuum_heap Breakpoint 7 at 0x6bdf2e: file vacuumlazy.c, line 1472. (gdb) c Continuing. Breakpoint 7, lazy_vacuum_heap (onerel=0x7f4c70d96688, vacrelstats=0x1873928) at vacuumlazy.c:1472 1472 Buffer vmbuffer = InvalidBuffer; (gdb)
輸入參數
1-relation
(gdb) p *onerel $14 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50820}, rd_smgr = 0x18362e0, 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 = 0x7f4c70d95bb8, rd_att = 0x7f4c70d95cd0, 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 = 0x7f4c70d94820, 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 = 0x182a030}
2-vacrelstats
存在索引,pages總數為124,掃描pages為124,原存活tuple為9501,新tuples為9401,已刪除tuples為100,已刪除的tuples的ItemPointer存儲在dead_tuples數組中(大小為num_dead_tuples)
(gdb) p *vacrelstats $15 = {hasindex = true, old_rel_pages = 124, rel_pages = 124, scanned_pages = 124, pinskipped_pages = 0, frozenskipped_pages = 0, tupcount_pages = 124, old_live_tuples = 9501, new_rel_tuples = 9401, new_live_tuples = 9401, new_dead_tuples = 0, pages_removed = 0, tuples_deleted = 100, nonempty_pages = 124, num_dead_tuples = 100, max_dead_tuples = 36084, dead_tuples = 0x1884820, num_index_scans = 0, latestRemovedXid = 397073, lock_waiter_detected = false} (gdb)
1.初始化變量
(gdb) n 1474 pg_rusage_init(&ru0); (gdb) 1475 npages = 0; (gdb) 1477 tupindex = 0; (gdb) p ru0 $16 = {tv = {tv_sec = 1548743482, tv_usec = 626506}, ru = {ru_utime = {tv_sec = 0, tv_usec = 40060}, ru_stime = { tv_sec = 0, tv_usec = 114769}, {ru_maxrss = 8900, __ru_maxrss_word = 8900}, {ru_ixrss = 0, __ru_ixrss_word = 0}, { ru_idrss = 0, __ru_idrss_word = 0}, {ru_isrss = 0, __ru_isrss_word = 0}, {ru_minflt = 5455, __ru_minflt_word = 5455}, {ru_majflt = 0, __ru_majflt_word = 0}, {ru_nswap = 0, __ru_nswap_word = 0}, {ru_inblock = 2616, __ru_inblock_word = 2616}, {ru_oublock = 376, __ru_oublock_word = 376}, {ru_msgsnd = 0, __ru_msgsnd_word = 0}, { ru_msgrcv = 0, __ru_msgrcv_word = 0}, {ru_nsignals = 0, __ru_nsignals_word = 0}, {ru_nvcsw = 814, __ru_nvcsw_word = 814}, {ru_nivcsw = 2, __ru_nivcsw_word = 2}}}
2.遍歷vacrelstats->num_dead_tuples行指針數組(ItemPointer)
(gdb) n 1478 while (tupindex < vacrelstats->num_dead_tuples) (gdb)
2.1獲取塊號/讀取塊到緩沖區中
1485 vacuum_delay_point(); (gdb) 1487 tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]); (gdb) 1488 buf = ReadBufferExtended(onerel, MAIN_FORKNUM, tblk, RBM_NORMAL, (gdb) (gdb) p tblk $17 = 29 (gdb) p buf $18 = 175
2.2加鎖,如不成功,則處理下一個元組
1490 if (!ConditionalLockBufferForCleanup(buf)) (gdb)
2.3調用lazy_vacuum_page釋放空間,整理碎片
1496 tupindex = lazy_vacuum_page(onerel, tblk, buf, tupindex, vacrelstats, (gdb) p tupindex $1 = 0 (gdb) n 1500 page = BufferGetPage(buf); (gdb) p tupindex $2 = 2 (gdb)
2.4獲取page,獲取該page的空閑空間
(gdb) n 1500 page = BufferGetPage(buf); (gdb) p tupindex $2 = 2 (gdb) n 1501 freespace = PageGetHeapFreeSpace(page); (gdb)
2.5釋放緩沖,記錄空閑空間
(gdb) 1503 UnlockReleaseBuffer(buf); (gdb) 1504 RecordPageWithFreeSpace(onerel, tblk, freespace); (gdb) 1505 npages++; (gdb)
lazy_vacuum_page
進入lazy_vacuum_page函數
1496 tupindex = lazy_vacuum_page(onerel, tblk, buf, tupindex, vacrelstats, (gdb) p tblk $3 = 30 (gdb) p buf $4 = 178 (gdb) p tupindex $5 = 2 (gdb) (gdb) step lazy_vacuum_page (onerel=0x7f4c70d95570, blkno=30, buffer=178, tupindex=2, vacrelstats=0x18676a8, vmbuffer=0x7fffaef4a19c) at vacuumlazy.c:1535 1535 Page page = BufferGetPage(buffer); (gdb)
輸入參數:塊號/緩沖區編號/tuple數組下標以及vacrelstats(統計信息+輔助存儲信息,如廢棄元組數組等)
(gdb) p vacrelstats->dead_tuples[0] $6 = {ip_blkid = {bi_hi = 0, bi_lo = 29}, ip_posid = 168}
1.初始化相關變量
(gdb) n 1537 int uncnt = 0; (gdb) 1541 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno); (gdb) 1543 START_CRIT_SECTION(); (gdb) 1545 for (; tupindex < vacrelstats->num_dead_tuples; tupindex++) (gdb) p page $7 = (Page) 0x7f4c44f46380 "\001" (gdb) p *page $8 = 1 '\001' (gdb) p *(PageHeader *)page $9 = (PageHeader) 0x4ec2441800000001 (gdb) p *(PageHeader)page $10 = {pd_lsn = {xlogid = 1, xrecoff = 1321354264}, pd_checksum = 0, pd_flags = 1, pd_lower = 1188, pd_upper = 7856, pd_special = 8192, pd_pagesize_version = 8196, pd_prune_xid = 0, pd_linp = 0x7f4c44f46398} (gdb)
2.遍歷廢棄元組數組
2.1獲取塊號,如塊號不一致,跳出循環
2.2獲取偏移/行指針
2.3標記為未使用,記錄偏移
(gdb) n 1551 tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]); (gdb) 1552 if (tblk != blkno) (gdb) p tblk $11 = 30 (gdb) n 1554 toff = ItemPointerGetOffsetNumber(&vacrelstats->dead_tuples[tupindex]); (gdb) p vacrelstats->dead_tuples[tupindex] $12 = {ip_blkid = {bi_hi = 0, bi_lo = 30}, ip_posid = 162} (gdb) n 1555 itemid = PageGetItemId(page, toff); (gdb) p toff $13 = 162 (gdb) n 1556 ItemIdSetUnused(itemid); (gdb) p itemid $14 = (ItemId) 0x7f4c44f4661c (gdb) p *itemid $15 = {lp_off = 0, lp_flags = 3, lp_len = 0} (gdb) n 1557 unused[uncnt++] = toff; (gdb) 1545 for (; tupindex < vacrelstats->num_dead_tuples; tupindex++) (gdb)
3.調用PageRepairFragmentation整理碎片
3.1判斷和檢查(嚴謹的編碼!!!)
... (gdb) b vacuumlazy.c:1560 Breakpoint 2 at 0x6be604: file vacuumlazy.c, line 1560. (gdb) c Continuing. Breakpoint 2, lazy_vacuum_page (onerel=0x7f4c70d95570, blkno=30, buffer=178, tupindex=5, vacrelstats=0x18676a8, vmbuffer=0x7fffaef4a19c) at vacuumlazy.c:1560 1560 PageRepairFragmentation(page); (gdb) (gdb) step PageRepairFragmentation (page=0x7f4c44f46380 "\001") at bufpage.c:481 481 Offset pd_lower = ((PageHeader) page)->pd_lower; (gdb) n 482 Offset pd_upper = ((PageHeader) page)->pd_upper; (gdb) 483 Offset pd_special = ((PageHeader) page)->pd_special; (gdb) 500 if (pd_lower < SizeOfPageHeaderData || (gdb) p pd_lower $17 = 1188 (gdb) p pd_upper $18 = 7856 (gdb) p pd_special $19 = 8192 (gdb) n 501 pd_lower > pd_upper || (gdb) 502 pd_upper > pd_special || (gdb) 504 pd_special != MAXALIGN(pd_special)) (gdb) 503 pd_special > BLCKSZ ||
3.2獲取偏移,初始化變量
(gdb) 513 nline = PageGetMaxOffsetNumber(page); (gdb) n 514 itemidptr = itemidbase; (gdb) 515 nunused = totallen = 0; (gdb) p nline $20 = 291 (gdb) p *itemidptr $21 = {offsetindex = 162, itemoff = 8144, alignedlen = 48} (gdb)
3.3遍歷行指針數組
3.3.1獲取行指針lp
3.3.2如ItemId正在使用,記錄到itemidbase數組中;否則標記ItemId未被使用
(gdb) 516 for (i = FirstOffsetNumber; i <= nline; i++) (gdb) n 519 if (ItemIdIsUsed(lp)) (gdb) 539 ItemIdSetUnused(lp); (gdb) 540 nunused++; (gdb) 516 for (i = FirstOffsetNumber; i <= nline; i++) (gdb)
跳出循環,繼續執行
516 for (i = FirstOffsetNumber; i <= nline; i++) (gdb) b bufpage.c:544 Breakpoint 3 at 0x8b1d2d: file bufpage.c, line 544. (gdb) c Continuing. Breakpoint 3, PageRepairFragmentation (page=0x7f4c44f46380 "\001") at bufpage.c:544 544 nstorage = itemidptr - itemidbase; (gdb) (gdb) p nunused $22 = 284
3.4計算數組中存儲的元素個數
A.如個數為0,重置page
B.否則調用compactify_tuples壓縮頁
(gdb) n 545 if (nstorage == 0) (gdb) p nstorage $23 = 7 (gdb) n 553 if (totallen > (Size) (pd_special - pd_lower)) (gdb) 559 compactify_tuples(itemidbase, nstorage, page); (gdb)
3.5為PageAddItem方法設置標記位
(gdb) 563 if (nunused > 0) (gdb) 564 PageSetHasFreeLinePointers(page); (gdb) 567 } (gdb)
4.標記buffer為dirty
(gdb) lazy_vacuum_page (onerel=0x7f4c70d95570, blkno=30, buffer=178, tupindex=5, vacrelstats=0x18676a8, vmbuffer=0x7fffaef4a19c) at vacuumlazy.c:1565 1565 MarkBufferDirty(buffer); (gdb) n
5.寫入WAL Record
1568 if (RelationNeedsWAL(onerel)) (gdb) 1572 recptr = log_heap_clean(onerel, buffer, (gdb) 1576 PageSetLSN(page, recptr); (gdb) 1585 END_CRIT_SECTION();
6.如all-visible,則設置頁面all-visible標記
(gdb) n 1593 if (heap_page_is_all_visible(onerel, buffer, &visibility_cutoff_xid, (gdb) 1595 PageSetAllVisible(page); (gdb)
7.如page為all-visible,設置vm
1602 if (PageIsAllVisible(page)) (gdb) 1604 uint8 vm_status = visibilitymap_get_status(onerel, blkno, vmbuffer); (gdb) 1605 uint8 flags = 0; (gdb) 1608 if ((vm_status & VISIBILITYMAP_ALL_VISIBLE) == 0) (gdb) 1609 flags |= VISIBILITYMAP_ALL_VISIBLE; (gdb) 1610 if ((vm_status & VISIBILITYMAP_ALL_FROZEN) == 0 && all_frozen) (gdb) 1613 Assert(BufferIsValid(*vmbuffer)); (gdb) 1614 if (flags != 0) (gdb) 1615 visibilitymap_set(onerel, blkno, buffer, InvalidXLogRecPtr, (gdb)
8.返回下一個page的起始數組編號
(gdb) 1619 return tupindex; (gdb) p tupindex $24 = 5 (gdb)
到此,關于“PostgreSQL的vacuum過程中lazy_vacuum_heap函數有什么作用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。