本节简单介绍了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
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1142
183.71 KB下载数642
644.84 KB下载数2755