PostgreSQL的vacuum过程中lazy_vacuum_heap函数的作用是什么?

分类:编程技术 时间:2024-02-20 15:53 浏览:0 评论:0
0
本文主要介绍“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函数的实现逻辑,该函数访问堆表。将丢弃的元组标记为未使用,并压缩这些元组所在页面上的可用空间。

1.数据结构

Macro 定义
真空和分析命令选项

/* -------------------------- * 真空和分析分析语句 * 真空和分析命令选项 * * 即使这些名义上是两个语句,* 为两个语句仅使用一种节点类型也很方便。请注意,选项中必须至少设置 VACOPT_VACUUM * 和 VACOPT_ANALYZE 之一。 * 这里虽然有两种不同的说法,但是只需要使用统一的Node类型即可。 * 注意,选项中至少必须设置VACOPT_VACUUM/VACOPT_ANALYZE。 * ---- ------------------ */typedef enum VacuumOption{ VACOPT_VACUUM = 1 << 0, /* 执行 VACUUM */ VACOPT_ANALYZE = 1 << 1, / * 执行 ANALYZE */ VACOPT_VERBOSE = 1 << 2, /* 打印进度信息 */ VACOPT_FREEZE = 1 << 3, /* FREEZE 选项 */ VACOPT_FULL = 1 << 4, /* FULL(非并发)真空 */   VACOPT_SKIP_LOCKED = 1 << 5, /* 如果无法获取锁则跳过 */ VACOPT_SKIPTOAST = 1 << 6, /* 不处理 TOAST 表(如果有) */ VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don 不跳过任何页面 */} VacuumOption;

itemIdSort
PageRepairFragmentation/PageIndexMultiDelete 排序支持

/* * PageRepairFragmentation 和 PageRepairFragmentation 的排序支持PageIndexMultiDelete * PageRepairFragmentation/PageIndexMultiDelete 的排序支持 */typedef struct itemIdSortData{ //行指针数组索引 uint16 offsetindex; /* linp 数组索引 */  //item 数据页偏移量 int16 itemoff; /* 项数据的页偏移 */ //对齐长度 uint16alignedlen; /* MAXALIGN(item data len) */} itemIdSortData;//结构体指针 typedef itemIdSortData *itemIdSort ;

LVRelStats

typedef struct LVRelStats{ /* hasindex = true 表示两遍策略; false 表示单遍 */ //T 表示两遍策略,F 表示单遍策略 bool hasindex; /* 关于 rel 的整体统计 */ // rel 的全局统计 // pg_class.relpages 的前一个值 BlockNumber old_rel_pages; /* pg_class.relpages 的先前值 */ //Total 的页数 BlockNumber rel_pages; /* 总页数 */ //扫描页数 BlockNumber s canned_pa​​ges; /* 我们检查的页数 */ // 由于引脚而跳过的页数 BlockNumber pinskipped_pa​​ges; /* 由于 pin 导致我们跳过的页面数 */ // 跳过的冻结页面 BlockNumber freeze_pages; /* # 我们跳过的冻结页面 */ //计算其元组的页面 BlockNumber tupcount_pages; /* 我们统计了元组的页面 */ //pg_class.reltuples 的先前值 double old_live_tuples; /* pg_class.reltuples 的先前值 */ //新估计的元组总数 double new_rel_tuples; /* 新估计的元组总数 */ //新估计的活动元组数量 double new_live_tuples; /* 新估计的存活元组总数 */ //新估计的丢弃元组数量 double new_dead_tuples; /* 新估计的死元组总数 */ // 清除的页面 BlockNumber pages_removed; //删除的元组 double tuples_deleted; //实际上非空页 + 1 BlockNumber nonempty_pages; /* 实际上,最后一个非空页 + 1 */ /* 我们要删除的元组 TID 列表 */ / * 注意:这个列表按 TID 地址排序 */ // 要删除的元组 TID 链表 //注:链表已按TID地址排序 //当前条目数/条目数 int num_dead_tuples; /* current # of items */ //数组中分配的槽位(丢弃元组的最大数量) int max_dead_tuples;/* # array 中分配的槽位 */ //ItemPointer array ItemPointer dead_tuples; /* ItemPointerData 数组 */ //扫描的索引数量 int int num_index_scans; //最后清除的交易ID TransactionId Late stRemovedXid; //服务员存在吗? bool lock_waiter_detected;} LVRelStats;

ItemPointer
行指针

typedef struct ItemPointerData{ BlockIdData ip_blkid;//区块号 OffsetNumber ip_posid;//块内部偏移}typedef ItemPointerData *ItemPointer;

2。源代码解读

lazy_vacuum_heap
lazy_vacuum_heap将废弃的元组标记为未使用,并压缩这些元组所在页面上的可用空间。在此期间,被lazy_scan_heap标记为存活元组的页面将不会被访问。
主要处理流程如下:
1.初始化变量
2.遍历vacrelstats->num_dead_tuples行指针数组(ItemPointer)
2.1获取块号/将块读入缓冲区
2.2加锁,不成功则处理下一个tuple
2.3调用lazy_vacuum_page释放空间并进行碎片整理
2.4获取页面并获取页面的空闲空间
2.5释放缓冲区并记录空闲空间
3.收尾工作

/**lazy_vacuum_heap()-第二次遍历头部*lazy_vacuum_heap()-二次访问表**这个例程标记defyused,并操作在其Pages上释放出Free*Space。不访问没有从 *lazy_scan_heap 记录死元组的页面根本没有编辑。 *lazy_vacuum_heap 将废弃的元组标记为未使用,并压缩这些元组所在页面上的可用空间。在此期间,没有被lazy_scan_heap标记的丢弃元组的页面将不会被访问。 * * 注意:第二遍执行此操作的原因是,在删除元组的索引条目之前,我们无法删除元组,并且我们希望尽可能大地批量处理索引条目删除。 * 注意:两次访问堆表的原因是在清除索引项之前无法清除元组, * 并且我们要批量处理索引项,越大越好。 */static voidlazy_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;//Buffer Page Page page;//Page Size freespace;真空延迟点(); //获取区块编号 tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]); //扩展模式下读缓冲区 buf = ReadBufferExtended(onerel, MAIN_FORKNUM, tblk, RBM_NORMAL, vac_strategy) ; {                                                                                                                                  nue; ​/* 现在我们已经压缩了页面,记录其可用空间 */ //现在页面已被压缩(空间已释放),记录可用空间 Page = BufferGetPage(buf);弗雷espace = PageGetHeapFreeSpace(页); UnlockReleaseBuffer(buf);RecordPageWithFreeSpace(onerel, tblk, freespace); npages++; } if (BufferIsValid(vmbuffer)) { //释放缓冲区 ReleaseBuffer(vmbuffer); vmbuffer = 无效缓冲区; } ereport(elevel, (errmsg("\"%s\": 删除了 %d 页中的 %d 行版本", RelationGetRelationName(onerel), tupindex, npages), errdetail_internal("%s", pg_rusage_show(&ru0))) );}

lazy_vacuum_page
lazy_vacuum_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,则重置页面
B.否则,调用compactify_tuples压缩page
3.5 设置PageAddItem方法标记位
4。将缓冲区标记为脏
5。写入WAL记录
6.如果全部可见,则设置页面全部可见标志
7.如果页面全部可见,则设置 vm
8。返回下一页的起始数组编号

/* *lazy_vacuum_page() -- 释放页面上的死元组 * 并修复其碎片。 * lazy_vacuum_page() -- 释放页面中废弃的元组并对其进行碎片整理 * * 调用者必须持有缓冲区上的 pin 和缓冲区清理锁。 * 调用者必须持有缓冲区的 pin 和清理锁才能执行 * * tupindex 是该页的第一个死元组的 vacrelstats->dead_tuples 中的索引。我们假设其余的遵循 seq本质上。 * 返回值是本页元组之后的第一个tupindex。 vacrelstats->dead_tuples中丢弃的元组数量,我们假设剩余的元组是顺序的。 * 返回值是页面中tuple之后的第一个数字tupindex。 */static intlazy_vacuum_page(Relation onerel, BlockNumber blkno , Buffer buffer, int tupindex, LVRelStats *vacrelstats, Buffer *vmbuffer){ //获取页面 Page = BufferGetPage(buffer); OffsetNumberused[MaxOffsetNumber];//偏移数组 int uncnt = 0; Transaction Idvisibility_cutoff_xid;//交易ID bool all_frozen;//释放所有冻结 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno); //进入按键处理部分 START_CRIT_SECTION(); //遍历废弃的tuple数组 for (; tupindex < vacrelstats->num_dead_tuples; tupindex++) { BlockNumbertblk;//区块号 OffsetNumber toff;//偏移ItemId itemid;//行指针 //根据行指针获取区块号tblk = 项目指针GetBlockNumber(&vacrelstats->dead_tuples[tupindex]); if (tblk != blkno) // 不是同一个块,跳出循环 BREAK;/*Past End of Tuples for this block*/// 获取偏移量 TOFF = ITEMPOINTERGETOFFSETNUMBER (& Vacrelstats->DEAD_TUPLES [Tupindex]) ; & n BSP; //获取行指针 itemid = PageGetItemId(page, toff); //标记为未使用 ItemIdSetUnused(itemid); //记录偏移量used[uncnt++] = tooff; } //对其进行碎片整理 PageRepairFragmentation(page); /* * 在写入 WAL 之前将缓冲区标记为脏。 * 将缓冲区标记为脏 */ MarkBufferDirty(buffer); /* XLOG 内容 */ if (RelationNeedsWAL(onerel)) { //记录 WAL 记录 XLogRecPtr recptr; recptr = log_heap_clean(onerel, 缓冲区, 未使用, uncnt, vacrelstats->latestRemovedXid); PageSetLSN(页面,recptr); /* * 结束关键部分,这样我们就可以安全地进行可见性测试(这 * 可能需要执行 IO 并分配内存!)。如果我们现在崩溃,页面(包括相应的虚拟机位)可能不会被标记为全部可见,但这没关系。迟到了r 真空可以解决这个问题。 * 结束临界区,这样我们就可以安全地执行可见性检查 * (这可能需要执行IO/分配内存) * 如果进程崩溃,该页面(包括相应的vm位)可能会被标记为所有可见,但没有问题,真空将在未来修复。 */ END_CRIT_SECTION(); /* * 现在我们已经从页面中删除了死元组,再次 * 检查页面是否已变得全部可见。该页面已被标记为脏、独占锁定,并且如果需要,整个页面图像已在上面的 log_heap_clean() 中发出。 * 现在,我们从页面中删除了过时的元组,再次检查页面现在是否全部可见。 * 该页面已被标记为脏并被独占锁定。如果需要,完整的页面映像将被记录在log_heap_clean()中。 */ if (heap_page_is_all_visible(onerel, buffer, &visibility_cutoff_xid, &all_frozen))   PageSetAllVisible(page); /* * 堆页的所有变化已经完成了。如果现在设置了全部可见标志,则还设置 VM 全部可见位(如果可能的话,设置全部冻结位),除非之前已完成此操作。 * 对堆页的所有修改均已完成。如果设置了全部可见标志,则也会设置 VM 全部可见位*(并且,如果可能,设置全部冻结位),除非之前已完成。 */ if (PageIsAllVisible(page)) { uint8 vm_status = Visibilitymap_get_status(onerel, blkno, vmbuffer); uint8 标志 = 0; /* 如果需要,将 VM 全冻结位设置为标志 */ //如果需要,将 VM 全冻结标志位设置 if ((vm_status & VISIBILITYMAP_ALL_VISIBLE) ==0) flags |= VISIBILITYMAP_ALL_VISIBLE ; if ((vm_status & VISIBILITYMAP_ALL_FROZEN) == 0 && all_frozen)          标志 |= VISIBILITYMAP_ALL_FROZEN; 0)visibilitymap_set(onerel,blkno,缓冲区,InvalidXLogRecPtr,*vmbuffer,visibility_cutoff_xid、标志); } return tupindex;}/* * PageRepairFragmentation * * 释放页面上的碎片空间。 * 释放页面上的碎片空间。 * * 它不会删除未使用的行指针!请不要改变这个。 * 此方法不会清除未使用的行指针!因此,请勿修改它。 * * 此例程仅适用于堆页,但请参阅 PageIndexMultiDelete。 * 该方法仅使用在堆页上,但注意参考PageIndexMultiDelete。 * * 作为副作用,页面的 PD_HAS_FREE_LINES 提示位被更新。 * 当该方法处理时,页面的PD_HAS_FREE_LINES标志位将会被更新。 * */voidPageRepairFragmentation(Page page){ 偏移量 pd_lower = ((PageHeader) page)->pd_lower ;偏移量pd_upper = ((PageHeader)页)->pd_upper;偏移量 pd_special = ((PageHeader) 页)->pd_special; itemIdSortData itemidbase[MaxHeapTuplesPerPage];//存储数据 itemIdSort itemidptr;项目 ID lp; int nline、nstorage、nunused;整数我;尺寸总长; /* * 这是值得的这里比大多数地方更加偏执,*因为我们即将重新整理(通常是)共享磁盘缓冲区中的数据。如果我们不小心,那么损坏的指针、长度等可能会导致我们破坏相邻的磁盘缓冲区,从而进一步扩大数据丢失。因此,请检查所有内容。 * 在这里比其他地方执行更多的检查是值得的,因为我们将在(通常)共享磁盘缓冲区中重新排列数据。 * * 如果我们不小心,损坏的行指针、数据长度等可能会导致与相邻磁盘缓冲区的冲突, ** 如果错误进一步传播,则会导致数据丢失。因此,需要仔细检查。 */ 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("损坏的页面指针:较低= %u,上层 = %u,特殊 = %u",pd_lower,pd_upper,pd_special))); /* * 遍历行指针数组 and 收集有关活动项目的数据。 * 遍历行指针数组并收集幸存的条目。 */ nline = PageGetMaxOffsetNumber(page);//获取最大偏移量 itemidptr = itemidbase;// nunused = Totallen = 0; for (i = FirstOffsetNumber; i <= nline; i++) {   //------------ 遍历行指针数组 //获取行指针 lp = PageGetItemId(page, i); if (ItemIdIsUsed(lp)) { //如果ItemId正在使用 if (ItemIdHasStorage(lp)) {//如果itemid与存储相关,判断条件:((Itemid)->LP_LEN != 0) Itemidptr->OffsetEx = i-1;itemidptr->itemoff = ItemIdGetOffset(lp); //执行判断 if (unlikely(itemidptr->itemoff < (int) pd_upper || itemidpt r->itemoff >= (int) pd_special)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED) ,                                  errmsg("corrupted item point: % u", Itemidptr->alignedlen = MAXALI); itemidptr->alignedlen = MAXALI GN(ItemIdGetLength(lp));                                                                                          使用‐‐          ‐‐   ‐                                                                     使用 ‐ s s s s s ‐ 到 ‐‐‐‐​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ 1 个元素                            p; } else else { /* 未使用的条目应该有 lp_len = 0,但要确保 */ //Unused ItemId ItemIdSetUnused(lp);未使用++; } } //数组中存储的元素数量 nstorage = itemidptr - itemidbase; if (nstorage == 0) { /* 页面完全为空,所以只需快速重置 */  //页面完全为空,重置页面 ((PageHeader) page)->pd_upper = pd_special; } else { if (totallen > (Size) (pd_special-pd_lower)) EREPORT (ERRCode (ErCode_data_Corrupted), ErRMSG (“损坏的项目长度:总计%u,可用空间%u”,(unsigned int)totallen,pd_special - pd_lower) )); Compactify_tuples(itemidbase,nstorage,页面); } /* Sethint bit for PageAddItem */ //设置PageAddItem方法的标记位 if ( nunused > 0) //有未使用的空格,设置标记PageSetHasFreeLinePoint呃(页); else //清除标记 PageClearHasFreeLinePointers(page);}/* * 删除或标记一些未使用的行指针后,移动元组以 * 删除因删除项而造成的间隙。 itemidbase, int nitems, Page page){ PageHeader phdr = (PageHeader) page;偏置鞋面;整数我; /* 将 itemIdSortData 数组排序为 itemoff 递减顺序 */ //按照 itemoff 的降序对 itemIdSortData 数组进行排序的方式 qsort((char *) itemidbase, nitems, sizeof(itemIdSortData), itemoffcompare); //重新组织页面 upper = phdr->pd_special; for (i = 0; i < nitems; i++) { itemIdSort itemidptr = &itemidbase[i];项目 ID lp; lp = PageGetItemId(页, 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 * 设置项目标识符为UNUSED,不存储。 * 谨防itemId的多次评估! * 设置项目ID为未使用。 */#define Item IdSetUnused(itemId) \ ( \ (itemId)->lp_flags = LP_UNUSED, \ (itemId)->lp_off = 0, \ (itemId)->lp_len = 0 \)

3.跟踪分析

测试脚本:删除数据并执行vacuum

11:04:59 (xdb@[local ]:5432)testdb=# delete from t1 where id < 600;DELETE 10014:26:16 (xdb@[本地]:5432)testdb=# 检查点;CHECKPOINT11:18:29 (xdb@[本地]:5432)testdb=# 真空详细 t1;

lazy_vacuum_heap
启动gdb并设置断点

 (gdb) b lazy_vacuum_heapBreakpoint 7 at 0x6bdf2e: 文件vacuumlazy.c,第1472行。(gdb) cContinuing.Breakpoint 7,lazy_vacuum_heap (onerel= 0x7f4c70d96688, vacrelstats=0x1873928) at vac uumlazy.c:14721472 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 = 508 20、rd_lockInfo = {lockRelId = { rel Id = 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_p dcxt = 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_am 处理程序 = 0、rd_indexcxt = 0x0、rd_amroutine = 0x0、rd_opfamily = 0x0、rd_opcintype = 0x0、rd_support = 0x0、rd_supportinfo = 0x0、rd_ind选项 = 0x0,rd_indexprs = 0x0、rd_indpred = 0x0、rd_exclops = 0x0、rd_exclprocs = 0x0、rd_exclstrats = 0 x0, rd_amcache = 0x0, rd_indcollat​​ion = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x182a030}

2-vacrelstats
保存索引中,总页数为124 ,扫描的页数为124,原来幸存的元组为9501,新的元组为9401,删除的元组为100,删除的元组的ItemPointer存储在dead_tuples数组中(大小为num_dead_tuples)

(gdb) p *vacrelstats$15 = {hasindex = true,old_rel_pages = 124,rel_pages = 124,scanned_pa​​ges = 124,pinskipped_pa​​ges = 0,frozen_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)
< p >1.初始化变量

(gdb) n1474 pg_rusage_init(&ru0);(gdb) 1475 np年龄 = 0;(gdb) 1477 tupindex = 0;(gdb) p ru0$16 = {tv = {tv_sec = 1548 743482,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 _字= 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_niv csw_字=2} }}

2.遍历vacrelstats->num_dead_tuples行指针数组(ItemPointer)

(gdb) n1478 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(one rel, MAIN_FORKNUM, tblk, RBM_NORMAL,(gdb) (gdb) p tb 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) n1500   page = buffergetpage(buf); ( GDB) P Tupindex $ 2 = 2 (GDB) 

2.4 获取Page,获取Page的空闲空间

 (GDB) N1500 Page = BufferGetPage(buf);(gdb) p tupindex $2 = 2(gdb) n1501 freespace = PageGetHeapFreeSpace(page);(gdb)

2.5 释放缓冲区并记录可用空间

(gdb ) 1503 UnlockReleaseBuffer(buf);(gdb)第1504章 1505、第1505章 1505、第1505章>输入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)steplazy_vacuum_page(一个rel = 0x7f4c70d95570,blkno = 30,buffer = 178,tupindex = 2,vacrelstats = 0x18676a8,vmbuffer = 0x7fffaef4a19c)在vacuumlazy.c:15351535页面页= BufferGetPage(缓冲区);(gdb) 

输入参数:块号/缓冲区号/元组数组下标和vacrelstats(统计信息+辅助存储信息,如丢弃的元组数组等)

(gdb) p vacrelstats ->dead_tuples[0]$6 = {ip_blkid = {bi_hi = 0, bi_lo = 29}, ip_posid = 168}

1.初始化相关变量

(gdb) n1537 int   uncnt = 0;(gdb) 1541 pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_VACUUMED, blkno);(gdb) 1543 START_CRIT_SECTION();(gdb) 1545 for (; tup 索引 < vacrelstats->num_dead_tuples; tupindex++)(gdb) p 页$7 = (页 ) 0 x7f4c44f46380 "\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) n1551 tblk = ItemPointerGetBlockNumber(&vacrelstats ->dead_tuples[tupindex]);(gdb) 1552 if (tblk != blkno) (gdb) p tblk$11 = 30(gdb) n1554 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) n1555 itemid = PageGetItemId(page, toff);(gdb) ptoff$13 = 162(gdb) n1556 ItemIdSetUnused( itemid);(gdb) p itemid$14 = (ItemId) 0x7f4c44f4661c(gdb) p *itemid$15 = {lp_off = 0, lp_flags = 3, lp _len = 0}(gdb) n1557 未使用 [uncnt++] = toff;(gdb) 1545 for (; tupindex < vacrelstats->num_dead_tuples; tupindex++)(gdb )

3.调用PageRepairFragmentation进行碎片整理
3.1判断与检查(严格编码!!!)

...(gdb) bvacuumlazy.c:1560Breakpoint 2 at 0x6be604:文件vacuumlazy.c,第1560行。 (gdb)cContinuing.Breakpoint 2,lazy_vacuum_page(onerel = 0x7f4c70d95570,blkno = 30,缓冲区= 178,tupindex = 5,vacrelstats = 0x18676a8,vmbuffer = 0x7fffaef4a19c)在vacuumlazy.c:15601560 PageRepairFragmentation(页面);(g数据库)(gdb ) stepPageRepairFragmentation (page=0x7f4c44f46380 "\001") at bufpage.c:481481 偏移量 pd_lower = ((PageHeader) page)->pd_lower; (gdb) n482 偏移量 pd_upper = ((PageHeader) 页)->pd_upper;(gdb) 483 偏移量 pd_special = ((PageHeader) 页)->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) n501 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 (页); (GDB) N514 Itemidptr = Itemidbase; (GDB) 515 未使用 = 0; (GDB)P nlin。 e $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) n519 if (ItemIdIsUsed(lp))(gdb) 539 ItemIdSetUnused(lp );(gdb) 540 未使用++; (gdb) 516 for (i = FirstOffsetNumber; i < = nline; i++)(gdb)

跳出循环继续执行

516 for (i = FirstOffsetNumber; i <= nline; i++)(gdb) b bufpage.c:544Breakpoint 3 at 0x8b1d2d: 文件 bufpage.c,第 544 行。(gdb) cContinuing.Breakpoint 3、PageRepairFragmentation (page=0x7f4c44f46380 "\001") at bufpage.c:544544 nstorage = itemidptr - itemidbase;(gdb) (gdb) p nunused$22 = 284

3.4 计算存储的元素数量数组中
A、如果数量为0,则重置页面
B、否则,调用compactify_tuples压缩页面

(gdb) n545 if (nstorage == 0)(gdb ) p nstorage$23 = 7(gdb) n553 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.将缓冲区标记为脏

(gdb)lazy_vacuum_page(onerel = 0x7f4c70d95570,blkno = 30,buffer = 178,tupindex = 5,vacrelstats = 0x18676a8,vmbuffer = 0x7fffaef4a19c)在vacuumlazy.c:15651565 MarkBufferDirty(缓冲区) ;(gdb) n

5.写入 WAL 记录

1568 if (RelationNeedsWAL(onerel))(gdb) 1572 recptr = log_heap _clean (onerel,缓冲区,(gdb)1576 PageSetLSN(页,recptr);(gdb)1585 END_CRIT_SECTION();

6。如果全部可见,设置页面全部可见标记

< pre>(gdb) n1593 if (heap_page_is_all_visible(onerel, buffer, &visibility_cutoff_xid,(gdb) 1595 PageSetAllVisible(page);(gdb)< p>7. 如果页面全部可见,则设置 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          标志 |= VISIBILITYMAP_ALL_VISIBLE;(gdb) 1610 ​   断言(BufferIsValid(*vmbuffer));(gdb) 1614 if (标志!= 0)(gdb) 1615visibilitymap_set(onerel, blkno, buffer, InvalidXLogRecPtr,(gdb)

8.返回next页面起始数组编号

(gdb) 1619 return tupindex; (gdb) p tupindex$24 = 5(gdb)

就是这样了,关于《PostgreSQL》的“lazy_vacuum_heap函数在vacuum过程中的作用是什么”的研究是超过。希望能够解答大家的疑惑。理论与实践相结合,能够更好的帮助大家学习,去尝试吧!如果您想继续学习更多相关知识,请继续关注网站,小编会继续努力为您带来更多实用文章!

1. 本站所有资源来源于用户上传或网络,仅作为参考研究使用,如有侵权请邮件联系站长!
2. 本站积分货币获取途径以及用途的解读,想在本站混的好,请务必认真阅读!
3. 本站强烈打击盗版/破解等有损他人权益和违法作为,请各位会员支持正版!
4. 编程技术 > PostgreSQL的vacuum过程中lazy_vacuum_heap函数的作用是什么?

用户评论