这篇文章主要介绍“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命令选项
typedef enum VacuumOption
{
VACOPT_VACUUM = 1 << 0,
VACOPT_ANALYZE = 1 << 1,
VACOPT_VERBOSE = 1 << 2,
VACOPT_FREEZE = 1 << 3,
VACOPT_FULL = 1 << 4,
VACOPT_SKIP_LOCKED = 1 << 5,
VACOPT_SKIPTOAST = 1 << 6,
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7
} VacuumOption;
itemIdSort
PageRepairFragmentation/PageIndexMultiDelete的排序支持
typedef struct itemIdSortData
{
//行指针数组索引
uint16 offsetindex;
//item数据页内偏移
int16 itemoff;
//对齐长度
uint16 alignedlen;
} itemIdSortData;
//结构体指针
typedef itemIdSortData *itemIdSort;
LVRelStats
typedef struct LVRelStats
{
//T表示two-pass strategy,F表示one-pass strategy
bool hasindex;
//rel的全局统计信息
//pg_class.relpages的上一个值
BlockNumber old_rel_pages;
//pages的总数
BlockNumber rel_pages;
//扫描的pages
BlockNumber scanned_pages;
//由于pin跳过的pages
BlockNumber pinskipped_pages;
//跳过的frozen pages
BlockNumber frozenskipped_pages;
//计算其元组的pages
BlockNumber tupcount_pages;
//pg_class.reltuples的前值
double old_live_tuples;
//新估算的总元组数
double new_rel_tuples;
//新估算的存活元组数
double new_live_tuples;
//新估算的废弃元组数
double new_dead_tuples;
//已清除的pages
BlockNumber pages_removed;
//已删除的tuples
double tuples_deleted;
//实际上是非空page + 1
BlockNumber nonempty_pages;
//将要删除的元组TIDs链表
//注意:该链表已使用TID地址排序
//当前的入口/条目数
int num_dead_tuples;
//数组中已分配的slots(最大已废弃元组数)
int max_dead_tuples;
//ItemPointer数组
ItemPointer dead_tuples;
//扫描的索引数
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.收尾工作
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);
//现在已经压缩了页面(释放了空间),记录可用空间
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的起始数组编号
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;
//获取偏移
toff = ItemPointerGetOffsetNumber(&vacrelstats->dead_tuples[tupindex]);
//获取行指针
itemid = PageGetItemId(page, toff);
//标记为未使用
ItemIdSetUnused(itemid);
//记录偏移
unused[uncnt++] = toff;
}
//整理碎片
PageRepairFragmentation(page);
MarkBufferDirty(buffer);
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_CRIT_SECTION();
if (heap_page_is_all_visible(onerel, buffer, &visibility_cutoff_xid,
&all_frozen))
PageSetAllVisible(page);
if (PageIsAllVisible(page))
{
uint8 vm_status = visibilitymap_get_status(onerel, blkno, vmbuffer);
uint8 flags = 0;
//如需要,设置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;
}
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;
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)));
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
{
//未使用的ItemId
ItemIdSetUnused(lp);
nunused++;
}
}
//数组中存储的元素个数
nstorage = itemidptr - itemidbase;
if (nstorage == 0)
{
//page完全是空的,重置page
((PageHeader) page)->pd_upper = pd_special;
}
else
{
//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);
}
//为PageAddItem方法设置标记位
if (nunused > 0)
//存在未使用的空位,设置标记
PageSetHasFreeLinePointers(page);
else
//清除标记
PageClearHasFreeLinePointers(page);
}
static void
compactify_tuples(itemIdSort itemidbase, int nitems, Page page)
{
PageHeader phdr = (PageHeader) page;
Offset upper;
int i;
//以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;
}
#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函数有什么作用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!