文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

PostgreSQL 源码解读(129)- MVCC#13(vacuum过程-vacuum_set_xid_limits函数)

2024-04-02 19:55

关注

本节简单介绍了PostgreSQL手工执行vacuum的处理流程,主要分析了ExecVacuum->vacuum->vacuum_rel->heap_vacuum_rel->vacuum_set_xid_limits函数的实现逻辑,该函数计算最老的xmin和冻结截止点。

一、数据结构

宏定义
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;

二、源码解读

vacuum_set_xid_limits() — 计算最老的xmin和冻结截止点
大体逻辑如下:
1.获取最旧的XMIN(*oldestXmin)
2.计算冻结上限XID(freezeLimit)
3.计算multiXactCutoff
4.计算全表扫描上限XID(xidFullScanLimit)
5.计算mxactFullScanLimit




void
vacuum_set_xid_limits(Relation rel,
                      int freeze_min_age,
                      int freeze_table_age,
                      int multixact_freeze_min_age,
                      int multixact_freeze_table_age,
                      TransactionId *oldestXmin,
                      TransactionId *freezeLimit,
                      TransactionId *xidFullScanLimit,
                      MultiXactId *multiXactCutoff,
                      MultiXactId *mxactFullScanLimit)
{
    int         freezemin;
    int         mxid_freezemin;
    int         effective_multixact_freeze_max_age;
    TransactionId limit;
    TransactionId safeLimit;
    MultiXactId mxactLimit;
    MultiXactId safeMxactLimit;
    
    //获取最旧的XMIN
    *oldestXmin =
        TransactionIdLimitedForOldSnapshots(GetOldestXmin(rel, PROCARRAY_FLAGS_VACUUM), rel);
    Assert(TransactionIdIsNormal(*oldestXmin));
    
    freezemin = freeze_min_age;
    if (freezemin < 0)
        freezemin = vacuum_freeze_min_age;
    freezemin = Min(freezemin, autovacuum_freeze_max_age / 2);
    Assert(freezemin >= 0);
    
    limit = *oldestXmin - freezemin;
    if (!TransactionIdIsNormal(limit))
        limit = FirstNormalTransactionId;
    
    //安全上限
    safeLimit = ReadNewTransactionId() - autovacuum_freeze_max_age;
    if (!TransactionIdIsNormal(safeLimit))
        safeLimit = FirstNormalTransactionId;
    //上限比安全上限小
    if (TransactionIdPrecedes(limit, safeLimit))
    {
        ereport(WARNING,
                (errmsg("oldest xmin is far in the past"),
                 errhint("Close open transactions soon to avoid wraparound problems.\n"
                         "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
        limit = *oldestXmin;
    }
    //
    *freezeLimit = limit;
    
    effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
    
    mxid_freezemin = multixact_freeze_min_age;
    if (mxid_freezemin < 0)
        mxid_freezemin = vacuum_multixact_freeze_min_age;
    mxid_freezemin = Min(mxid_freezemin,
                         effective_multixact_freeze_max_age / 2);
    Assert(mxid_freezemin >= 0);
    
    //计算阶段的multi,注意需要生成一个有效值
    mxactLimit = GetOldestMultiXactId() - mxid_freezemin;
    if (mxactLimit < FirstMultiXactId)
        mxactLimit = FirstMultiXactId;
    safeMxactLimit =
        ReadNextMultiXactId() - effective_multixact_freeze_max_age;
    if (safeMxactLimit < FirstMultiXactId)
        safeMxactLimit = FirstMultiXactId;
    if (MultiXactIdPrecedes(mxactLimit, safeMxactLimit))
    {
        ereport(WARNING,
                (errmsg("oldest multixact is far in the past"),
                 errhint("Close open transactions with multixacts soon to avoid wraparound problems.")));
        mxactLimit = safeMxactLimit;
    }
    *multiXactCutoff = mxactLimit;
    if (xidFullScanLimit != NULL)
    {
        int         freezetable;
        Assert(mxactFullScanLimit != NULL);
        
        freezetable = freeze_table_age;
        if (freezetable < 0)
            freezetable = vacuum_freeze_table_age;
        freezetable = Min(freezetable, autovacuum_freeze_max_age * 0.95);
        Assert(freezetable >= 0);
        
        limit = ReadNewTransactionId() - freezetable;
        if (!TransactionIdIsNormal(limit))
            limit = FirstNormalTransactionId;
        *xidFullScanLimit = limit;
        
        freezetable = multixact_freeze_table_age;
        if (freezetable < 0)
            freezetable = vacuum_multixact_freeze_table_age;
        freezetable = Min(freezetable,
                          effective_multixact_freeze_max_age * 0.95);
        Assert(freezetable >= 0);
        
        mxactLimit = ReadNextMultiXactId() - freezetable;
        if (mxactLimit < FirstMultiXactId)
            mxactLimit = FirstMultiXactId;
        *mxactFullScanLimit = mxactLimit;
    }
    else
    {
        Assert(mxactFullScanLimit == NULL);
    }
}

三、跟踪分析

测试脚本



11:12:53 (xdb@[local]:5432)testdb=# vacuum t1;

启动gdb,设置断点



(gdb) b vacuum_set_xid_limits
Breakpoint 1 at 0x6ba463: file vacuum.c, line 622.
(gdb) c
Continuing.
Breakpoint 1, vacuum_set_xid_limits (rel=0x7fdb230b39a0, freeze_min_age=-1, freeze_table_age=-1, 
    multixact_freeze_min_age=-1, multixact_freeze_table_age=-1, oldestXmin=0xf88148 <OldestXmin>, 
    freezeLimit=0xf8814c <FreezeLimit>, xidFullScanLimit=0x7fffc5163b10, multiXactCutoff=0xf88150 <MultiXactCutoff>, 
    mxactFullScanLimit=0x7fffc5163b0c) at vacuum.c:622
622         TransactionIdLimitedForOldSnapshots(GetOldestXmin(rel, PROCARRAY_FLAGS_VACUUM), rel);
(gdb)

输入参数
freeze_min_age等为默认值



(gdb) p *rel
$1 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50820}, rd_smgr = 0x0, rd_refcnt = 1, rd_backend = -1, 
  rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 0 '\000', rd_statvalid = false, 
  rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7fdb230b3bb8, rd_att = 0x7fdb230b3cd0, 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 = 0x0, 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 = 0x28dc030}
(gdb)

获取最旧的XMIN(*oldestXmin)



(gdb) n
621     *oldestXmin =
(gdb) 
624     Assert(TransactionIdIsNormal(*oldestXmin));
(gdb) p *oldestXmin
$2 = 315793
(gdb)

计算冻结上限XID(freezeLimit)



(gdb) n
632     freezemin = freeze_min_age;
(gdb) 
633     if (freezemin < 0)
(gdb) p freezemin
$3 = -1
(gdb) n
634         freezemin = vacuum_freeze_min_age;
(gdb) 
635     freezemin = Min(freezemin, autovacuum_freeze_max_age / 2);
(gdb) p vacuum_freeze_min_age --> 默认值为五千万/50,000,000
$4 = 50000000
(gdb) p autovacuum_freeze_max_age --> 默认值为两亿/200,000,000
$5 = 200000000
(gdb) n
636     Assert(freezemin >= 0);
(gdb) p freezemin
$6 = 50000000
(gdb) n

A.limit = *oldestXmin - freezemin
B.获取新的事务号,判断oldestXmin是否年代久远(长期未提交的事务)
C.判断limit是否在saftlimit之前,比较方法是:
int32(limit - safeLimit) < 0? 即 int32(4245283089 - 4095283090) < 0?该断言为F,不成立.



641     limit = *oldestXmin - freezemin; --> 上限 = *oldestXmin - freezemin
(gdb) 
642     if (!TransactionIdIsNormal(limit))
(gdb) p limit
$7 = 4245283089
(gdb) n
650     safeLimit = ReadNewTransactionId() - autovacuum_freeze_max_age;
(gdb) p ReadNewTransactionId() --> 新的事务号,判断oldestXmin是否年代久远(长期未提交的事务)
$8 = 315794
(gdb) n
651     if (!TransactionIdIsNormal(safeLimit))
(gdb) p safeLimit
$9 = 4095283090
(gdb) n
654     if (TransactionIdPrecedes(limit, safeLimit)) 
(gdb) 
663     *freezeLimit = limit;
(gdb) 
670     effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
(gdb)

计算multiXactCutoff



(gdb) 
678     mxid_freezemin = multixact_freeze_min_age;
(gdb) 
679     if (mxid_freezemin < 0)
(gdb) 
680         mxid_freezemin = vacuum_multixact_freeze_min_age;
(gdb) 
681     mxid_freezemin = Min(mxid_freezemin,
(gdb) 
683     Assert(mxid_freezemin >= 0);
(gdb) 
686     mxactLimit = GetOldestMultiXactId() - mxid_freezemin;
(gdb) 
687     if (mxactLimit < FirstMultiXactId)
(gdb) 
691         ReadNextMultiXactId() - effective_multixact_freeze_max_age;
(gdb) 
690     safeMxactLimit =
(gdb) 
692     if (safeMxactLimit < FirstMultiXactId)
(gdb) 
695     if (MultiXactIdPrecedes(mxactLimit, safeMxactLimit))
(gdb) 
703     *multiXactCutoff = mxactLimit;
(gdb) 
705     if (xidFullScanLimit != NULL)
(gdb)

计算全表扫描上限XID(xidFullScanLimit)



(gdb) 
709         Assert(mxactFullScanLimit != NULL);
(gdb) 
718         freezetable = freeze_table_age;
(gdb) 
719         if (freezetable < 0)
(gdb) p freezetable
$10 = -1
(gdb) n
720             freezetable = vacuum_freeze_table_age;
(gdb) 
721         freezetable = Min(freezetable, autovacuum_freeze_max_age * 0.95);
(gdb) p vacuum_freeze_table_age
$11 = 150000000
(gdb) p autovacuum_freeze_max_age * 0.95
$12 = 189999999.99999997
(gdb) n
722         Assert(freezetable >= 0);
(gdb) 
728         limit = ReadNewTransactionId() - freezetable;
(gdb) 
729         if (!TransactionIdIsNormal(limit))
(gdb) p limit
$13 = 4145283090
(gdb) p freezetable
$14 = 150000000
(gdb) n
732         *xidFullScanLimit = limit;
(gdb) 
742         freezetable = multixact_freeze_table_age;
(gdb)

计算mxactFullScanLimit



(gdb) n
743         if (freezetable < 0)
(gdb) 
744             freezetable = vacuum_multixact_freeze_table_age;
(gdb) 
745         freezetable = Min(freezetable,
(gdb) 
747         Assert(freezetable >= 0);
(gdb) 
753         mxactLimit = ReadNextMultiXactId() - freezetable;
(gdb) 
754         if (mxactLimit < FirstMultiXactId)
(gdb) 
757         *mxactFullScanLimit = mxactLimit;
(gdb) 
763 }
(gdb) p *mxactFullScanLimit
$15 = 4144967297
(gdb)

完成调用



(gdb) n
lazy_vacuum_rel (onerel=0x7fdb230b39a0, options=1, params=0x7fffc5163e60, bstrategy=0x292b708) at vacuumlazy.c:245
245     aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
(gdb)

DONE!

四、参考资料

PG Source Code

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 资料下载
  • 历年真题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯