本节简单介绍了PostgreSQL手工执行vacuum的主处理流程,主要分析了ExecVacuum->vacuum->vacuum_rel函数的实现逻辑。
一、数据结构
宏定义
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;
VacuumStmt
存储vacuum命令的option&Relation链表
typedef struct VacuumStmt
{
NodeTag type;//Tag
//VacuumOption位标记
int options;
//VacuumRelation链表,如为NIL-->所有Relation.
List *rels;
} VacuumStmt;
VacuumParams
vacuum命令参数
typedef struct VacuumParams
{
//最小freeze age,-1表示使用默认
int freeze_min_age;
//扫描整个table的freeze age
int freeze_table_age;
//最小的multixact freeze age,-1表示默认
int multixact_freeze_min_age;
//扫描全表的freeze age,-1表示默认
int multixact_freeze_table_age;
//是否强制wraparound?
bool is_wraparound;
//以毫秒为单位的最小执行阈值
int log_min_duration;
} VacuumParams;
VacuumRelation
VACUUM/ANALYZE命令的目标表信息
typedef struct VacuumRelation
{
NodeTag type;
RangeVar *relation;
Oid oid;
List *va_cols;
} VacuumRelation;
二、源码解读
vacuum_rel():vacuum one heap relation
大体逻辑如下:
1.启动事务,快照入栈,设置事务状态为PROC_IN_VACUUM
2.打开relation,请求合适的锁(FULL->AccessExclusiveLock,Concurrent->ShareUpdateExclusiveLock)
3.执行相应的检查(owner/relkind/临时表/分区表…)
4.执行TOAST相关处理
5.执行前期准备工作(切换user等)
6.执行实际的工作
6.1.FULL->cluster_rel
6.2.Concurrent->heap_vacuum_rel
7.执行收尾工作
8.如存在TOAST,在执行TOAST表的vacuum
static bool
vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
{
LOCKMODE lmode;
Relation onerel;
LockRelId onerelid;
Oid toast_relid;
Oid save_userid;
int save_sec_context;
int save_nestlevel;
Assert(params != NULL);
//vacuuming relation时开启一个事务
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
if (!(options & VACOPT_FULL))
{
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
MyPgXact->vacuumFlags |= PROC_IN_VACUUM;
if (params->is_wraparound)
MyPgXact->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
LWLockRelease(ProcArrayLock);
}
CHECK_FOR_INTERRUPTS();
lmode = (options & VACOPT_FULL) ? AccessExclusiveLock : ShareUpdateExclusiveLock;
//打开relation,获取合适的锁.
onerel = vacuum_open_relation(relid, relation, params, options, lmode);
//如relation不能被打开/锁定,则退出
if (!onerel)
{
PopActiveSnapshot();
CommitTransactionCommand();
return false;
}
if (!vacuum_is_relation_owner(RelationGetRelid(onerel),
onerel->rd_rel,
options & VACOPT_VACUUM))
{
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return false;
}
if (onerel->rd_rel->relkind != RELKIND_RELATION &&
onerel->rd_rel->relkind != RELKIND_MATVIEW &&
onerel->rd_rel->relkind != RELKIND_TOASTVALUE &&
onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
{
ereport(WARNING,
(errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
RelationGetRelationName(onerel))));
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return false;
}
if (RELATION_IS_OTHER_TEMP(onerel))
{
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return false;
}
if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return true;
}
onerelid = onerel->rd_lockInfo.lockRelId;
LockRelationIdForSession(&onerelid, lmode);
if (!(options & VACOPT_SKIPTOAST) && !(options & VACOPT_FULL))
toast_relid = onerel->rd_rel->reltoastrelid;
else
toast_relid = InvalidOid;
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);
save_nestlevel = NewGUCNestLevel();
if (options & VACOPT_FULL)
{
int cluster_options = 0;
//在vacuuming前关闭relation,持有锁直至commit
relation_close(onerel, NoLock);
onerel = NULL;
if ((options & VACOPT_VERBOSE) != 0)
cluster_options |= CLUOPT_VERBOSE;
//VACUUM FULL现在是CLUSTER的一个变体,详细请查看cluster.c
cluster_rel(relid, InvalidOid, cluster_options);
}
else
heap_vacuum_rel(onerel, options, params, vac_strategy);
//回滚索引函数修改的GUC参数
AtEOXact_GUC(false, save_nestlevel);
//恢复userid和安全上下文
SetUserIdAndSecContext(save_userid, save_sec_context);
//所有的工作已完成,但持有锁,直到提交
if (onerel)
relation_close(onerel, NoLock);
PopActiveSnapshot();
CommitTransactionCommand();
if (toast_relid != InvalidOid)
vacuum_rel(toast_relid, NULL, options, params);
UnlockRelationIdForSession(&onerelid, lmode);
//DONE!
return true;
}
三、跟踪分析
测试脚本
17:19:28 (xdb@[local]:5432)testdb=# vacuum t1;
启动gdb,设置断点
(gdb) b vacuum_rel
Breakpoint 2 at 0x6bb319: file vacuum.c, line 1310.
(gdb) c
Continuing.
Breakpoint 2, vacuum_rel (relid=42634, relation=0x22948d0, options=1, params=0x7fff403d8880) at vacuum.c:1310
1310 bool rel_lock = true;
(gdb)
输入参数
relid=42634 —> t1
relation=0x22948d0 —> t1
使用默认的vacuum参数
###
16:20:00 (xdb@[local]:5432)testdb=# select oid,relname,reltype from pg_class where oid=42634;
oid | relname | reltype
-------+---------+---------
42634 | t1 | 42636
(1 row)
###
(gdb) p *relation
$5 = {type = T_RangeVar, catalogname = 0x0, schemaname = 0x0, relname = 0x22948b0 "t1", inh = true,
relpersistence = 112 'p', alias = 0x0, location = 7}
(gdb)
(gdb) p *params
$6 = {freeze_min_age = -1, freeze_table_age = -1, multixact_freeze_min_age = -1, multixact_freeze_table_age = -1,
is_wraparound = false, log_min_duration = -1}
(gdb)
启动事务,快照入栈,设置事务状态为PROC_IN_VACUUM
(gdb) n
1312 Assert(params != NULL);
(gdb)
1315 StartTransactionCommand();
(gdb)
1321 PushActiveSnapshot(GetTransactionSnapshot());
(gdb)
1323 if (!(options & VACOPT_FULL))
(gdb)
1345 LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
(gdb)
1346 MyPgXact->vacuumFlags |= PROC_IN_VACUUM;
(gdb)
1347 if (params->is_wraparound)
(gdb)
1349 LWLockRelease(ProcArrayLock);
(gdb)
1356 CHECK_FOR_INTERRUPTS();
(gdb)
打开relation,请求合适的锁(FULL->AccessExclusiveLock,Concurrent->ShareUpdateExclusiveLock)
(gdb)
1363 lmode = (options & VACOPT_FULL) ? AccessExclusiveLock : ShareUpdateExclusiveLock;
(gdb)
1374 if (!(options & VACOPT_NOWAIT))
(gdb)
1375 onerel = try_relation_open(relid, lmode);
(gdb)
1388 if (!onerel)
(gdb) p *onerel
$7 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 42634}, 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 = 0x7f2d2571bbb8, rd_att = 0x7f2d25637268, rd_id = 42634,
rd_lockInfo = {lockRelId = {relId = 42634, 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 = 0x2313030}
(gdb)
执行相应的检查(owner/relkind/临时表/分区表…)
(gdb) n
1442 if (!(pg_class_ownercheck(RelationGetRelid(onerel), GetUserId()) ||
(gdb) p GetUserId()
$8 = 10
(gdb) p RelationGetRelid(onerel)
$9 = 42634
(gdb) n
1466 if (onerel->rd_rel->relkind != RELKIND_RELATION &&
(gdb)
1487 if (RELATION_IS_OTHER_TEMP(onerel))
(gdb)
1500 if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
(gdb)
1519 onerelid = onerel->rd_lockInfo.lockRelId;
(gdb)
1520 LockRelationIdForSession(&onerelid, lmode);
(gdb) p onerelid
$10 = {relId = 42634, dbId = 16402}
(gdb)
执行TOAST相关处理
(gdb) n
1527 if (!(options & VACOPT_SKIPTOAST) && !(options & VACOPT_FULL))
(gdb)
1528 toast_relid = onerel->rd_rel->reltoastrelid;
(gdb)
1538 GetUserIdAndSecContext(&save_userid, &save_sec_context);
(gdb) p toast_relid
$11 = 0
(gdb)
执行前期准备工作(切换user等)
(gdb) n
1539 SetUserIdAndSecContext(onerel->rd_rel->relowner,
(gdb)
1541 save_nestlevel = NewGUCNestLevel();
(gdb)
1546 if (options & VACOPT_FULL)
(gdb) p save_nestlevel
$12 = 2
(gdb) n
执行实际的工作
Concurrent->heap_vacuum_rel(11.1版本为lazy_vacuum_rel函数)
(gdb) n
1557 lazy_vacuum_rel(onerel, options, params, vac_strategy);
(gdb)
执行收尾工作
(gdb)
1560 AtEOXact_GUC(false, save_nestlevel);
(gdb)
1563 SetUserIdAndSecContext(save_userid, save_sec_context);
(gdb)
1566 if (onerel)
(gdb)
1567 relation_close(onerel, NoLock);
(gdb)
1572 PopActiveSnapshot();
(gdb)
1573 CommitTransactionCommand();
(gdb)
1582 if (toast_relid != InvalidOid)
(gdb)
如存在TOAST,在执行TOAST表的vacuum
(gdb)
1582 if (toast_relid != InvalidOid)
(gdb)
1588 UnlockRelationIdForSession(&onerelid, lmode);
(gdb)
1591 return true;
(gdb)
执行完成
1592 }
(gdb)
vacuum (options=1, relations=0x23525f0, params=0x7fff403d8880, bstrategy=0x2352478, isTopLevel=true) at vacuum.c:344
344 if (options & VACOPT_ANALYZE)
(gdb)
DONE!
四、参考资料
PG Source Code
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756