返回顶部
首页 > 资讯 > 数据库 >PostgreSQL的vacuum过程中lazy_vacuum_heap函数有什么作用
  • 622
分享到

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

2024-04-02 19:04:59 622人浏览 安东尼
摘要

这篇文章主要介绍“postgresql的vacuum过程中lazy_vacuum_heap函数有什么作用”,在日常操作中,相信很多人在Postgresql的vacuum过程中lazy_vacuum_heap

这篇文章主要介绍“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函数有什么作用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

您可能感兴趣的文档:

--结束END--

本文标题: PostgreSQL的vacuum过程中lazy_vacuum_heap函数有什么作用

本文链接: https://lsjlt.com/news/64241.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • PostgreSQL的vacuum过程中lazy_vacuum_heap函数有什么作用
    这篇文章主要介绍“PostgreSQL的vacuum过程中lazy_vacuum_heap函数有什么作用”,在日常操作中,相信很多人在PostgreSQL的vacuum过程中lazy_vacuum_heap...
    99+
    2024-04-02
  • PostgreSQL的vacuum过程中heap_vacuum_rel函数分析
    这篇文章主要介绍“PostgreSQL的vacuum过程中heap_vacuum_rel函数分析”,在日常操作中,相信很多人在PostgreSQL的vacuum过程中heap_vacuum_rel函数分析问...
    99+
    2024-04-02
  • PostgreSQL中vacuum过程HeapTupleSatisfiesVacuum函数分析
    本篇内容主要讲解“PostgreSQL中vacuum过程HeapTupleSatisfiesVacuum函数分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“P...
    99+
    2024-04-02
  • PostgreSQL中怎么监控VACUUM的处理过程
    这篇文章主要讲解了“PostgreSQL中怎么监控VACUUM的处理过程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中怎么监控VACUUM的处理过程”吧!概览PG的MV...
    99+
    2023-05-31
  • PostgreSQL中mdread函数有什么作用
    本篇内容主要讲解“PostgreSQL中mdread函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL中mdread函数有什么作用...
    99+
    2024-04-02
  • PostgreSQL中fsm_search函数有什么作用
    本篇内容介绍了“PostgreSQL中fsm_search函数有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能...
    99+
    2024-04-02
  • PostgreSQL中hash_search_with_hash_value函数有什么作用
    本篇内容主要讲解“PostgreSQL中hash_search_with_hash_value函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Pos...
    99+
    2024-04-02
  • PostgreSQL中RelationGetBufferForTuple函数有什么作用
    这篇文章主要讲解了“PostgreSQL中RelationGetBufferForTuple函数有什么作用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Po...
    99+
    2024-04-02
  • PostgreSQL中BufferAlloc函数有什么作用
    这篇文章主要介绍“PostgreSQL中BufferAlloc函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中BufferAlloc函数有什么作用问题上存在疑惑,小编查阅了各式资料,整理出...
    99+
    2024-04-02
  • PostgreSQL中StrategyGetBuffer函数有什么作用
    本篇内容介绍了“PostgreSQL中StrategyGetBuffer函数有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大...
    99+
    2024-04-02
  • PostgreSQL中BufTableInsert函数有什么作用
    本篇内容介绍了“PostgreSQL中BufTableInsert函数有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细...
    99+
    2024-04-02
  • PostgreSQL中ReadBuffer_common函数有什么作用
    这篇文章主要介绍“PostgreSQL中ReadBuffer_common函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中ReadBuffer_common函数有什么作用问题上存在疑惑,小...
    99+
    2024-04-02
  • PostgreSQL中create_index_path函数有什么作用
    本篇内容主要讲解“PostgreSQL中create_index_path函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL中cr...
    99+
    2024-04-02
  • PostgreSQL中set_base_rel_pathlists函数有什么作用
    这篇文章主要介绍“PostgreSQL中set_base_rel_pathlists函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中set_base_rel_pathlists函数有什么作...
    99+
    2024-04-02
  • PostgreSQL中grouping_planner函数有什么作用
    这篇文章主要介绍“PostgreSQL中grouping_planner函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中grouping_planner函数有什么作用问题上存在疑惑,小编查...
    99+
    2024-04-02
  • PostgreSQL中heap_insert函数有什么作用
    这篇文章主要讲解了“PostgreSQL中heap_insert函数有什么作用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中heap_...
    99+
    2024-04-02
  • PostgreSQL的dump函数有什么作用
    本篇内容主要讲解“PostgreSQL的dump函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL的dump函数有什么作用”吧!O...
    99+
    2024-04-02
  • PostgreSQL中fetch_upper_rel和get_cheapest_fractional_path函数有什么作用
    这篇文章主要介绍“PostgreSQL中fetch_upper_rel和get_cheapest_fractional_path函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中fetch_...
    99+
    2024-04-02
  • PostgreSQL中函数pg_blocking_pids的作用是什么
    本篇内容介绍了“PostgreSQL中函数pg_blocking_pids的作用是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大...
    99+
    2024-04-02
  • PostgreSQL中​ExecutePlan函数与ExecSeqScan函数的作用是什么
    这篇文章主要介绍“PostgreSQL中ExecutePlan函数与ExecSeqScan函数的作用是什么”,在日常操作中,相信很多人在PostgreSQL中ExecutePlan函数与ExecSeqSca...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作