本节简单介绍了PostgreSQL手工执行vacuum的处理流程,主要分析了ExecVacuum->vacuum->vacuum_rel->heap_vacuum_rel->lazy_scan_heap->lazy_vacuum_index函数的实现逻辑,该函数清理index relation。
一、数据结构
宏定义
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;
IndexVacuumInfo
传递给ambulkdelete/amvacuumcleanup的输入参数结构体
typedef struct IndexVacuumInfo
{
//index relation
Relation index;
//是否只是ANALYZE(没有实际的vacuum)
bool analyze_only;
//如为T,则num_heap_tuples是一个估算值
bool estimated_count;
//进度信息的日志等级
int message_level;
//在堆中仍存在的元组数
double num_heap_tuples;
//访问策略
BufferAccessStrategy strategy;
} IndexVacuumInfo;
IndexBulkDeleteResult
ambulkdelete/amvacuumcleanup返回的统计信息结构体
typedef struct IndexBulkDeleteResult
{
//index中剩下的pages
BlockNumber num_pages;
//在vacuum期间清除的元组数
BlockNumber pages_removed;
//num_index_tuples是一个估算值?
bool estimated_count;
//剩余的元组数
double num_index_tuples;
//在vacuum期间清除的元组数
double tuples_removed;
//索引中未使用的pages
BlockNumber pages_deleted;
//可重用的pages
BlockNumber pages_free;
} IndexBulkDeleteResult;
二、源码解读
lazy_vacuum_index
lazy_vacuum_index清理index relation,删除指向在vacrelstats->dead_tuples元组的索引条目,更新运行时统计信息.
主要逻辑如下:
1.初始化IndexVacuumInfo结构体变量
2.调用index_bulk_delete函数
3.报告进展
static void
lazy_vacuum_index(Relation indrel,
IndexBulkDeleteResult **stats,
LVRelStats *vacrelstats)
{
IndexVacuumInfo ivinfo;
PGRUsage ru0;
pg_rusage_init(&ru0);
ivinfo.index = indrel;
ivinfo.analyze_only = false;
ivinfo.estimated_count = true;
ivinfo.message_level = elevel;
//这里只能提供num_heap_tuples的近似值
ivinfo.num_heap_tuples = vacrelstats->old_live_tuples;
ivinfo.strategy = vac_strategy;
//执行批量删除
*stats = index_bulk_delete(&ivinfo, *stats,
lazy_tid_reaped, (void *) vacrelstats);
ereport(elevel,
(errmsg("scanned index \"%s\" to remove %d row versions",
RelationGetRelationName(indrel),
vacrelstats->num_dead_tuples),
errdetail_internal("%s", pg_rusage_show(&ru0))));
}
lazy_vacuum_index->index_bulk_delete
index_bulk_delete批量删除索引项,回调函数会给出main-heap元组是否将被删除,返回值是已预分配内存的统计信息结构体.
IndexBulkDeleteResult *
index_bulk_delete(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats,
IndexBulkDeleteCallback callback,
void *callback_state)
{
//获取relation
Relation indexRelation = info->index;
RELATION_CHECKS;
CHECK_REL_PROCEDURE(ambulkdelete);
//ambulkdelete指向的实际函数是btbulkdelete
return indexRelation->rd_indam->ambulkdelete(info, stats,
callback, callback_state);
}
lazy_vacuum_index->index_bulk_delete->…btbulkdelete
Index Relation的rd_amroutine->ambulkdelete,实际是btbulkdelete函数
IndexBulkDeleteResult *
btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
IndexBulkDeleteCallback callback, void *callback_state)
{
//relation
Relation rel = info->index;
BTCycleId cycleid;
//如果是第一次调用,则分配内存,否则重用已存在的结构体
if (stats == NULL)
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
//建立vacuum循环ID,用于本次扫描
//PG_ENSURE_ERROR_CLEANUP确保在发生故障时清理共享内存
PG_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
{
TransactionId oldestBtpoXact;//事务ID
//开始vacuum
cycleid = _bt_start_vacuum(rel);
//指向BTree vacuum扫描
btvacuumscan(info, stats, callback, callback_state, cycleid,
&oldestBtpoXact);
_bt_update_meta_cleanup_info(info->index, oldestBtpoXact,
info->num_heap_tuples);
}
PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
_bt_end_vacuum(rel);
//返回统计信息
return stats;
}
三、跟踪分析
测试脚本 : 删除数据,执行vacuum
10:08:46 (xdb@[local]:5432)testdb=# delete from t1 where id < 1200;
DELETE 100
11:26:03 (xdb@[local]:5432)testdb=# checkpoint;
CHECKPOINT
11:26:04 (xdb@[local]:5432)testdb=#
11:25:55 (xdb@[local]:5432)testdb=# vacuum t1;
启动gdb,设置断点
(gdb) b lazy_vacuum_index
Breakpoint 1 at 0x6bea40: file vacuumlazy.c, line 1689.
...
Breakpoint 1, lazy_vacuum_index (indrel=0x7f7334825050, stats=0x2aaffb8, vacrelstats=0x2aaf958) at vacuumlazy.c:1689
1689 pg_rusage_init(&ru0);
(gdb)
输入参数
(gdb) p *indrel
$6 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50823}, 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 = 0x7f733491ad20, rd_att = 0x7f733491a9b8, rd_id = 50823,
rd_lockInfo = {lockRelId = {relId = 50823, 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 = 0x7f733491a8d8, rd_indextuple = 0x7f733491a8a0,
rd_amhandler = 330, rd_indexcxt = 0x2a05340, rd_amroutine = 0x2a05480, rd_opfamily = 0x2a05598, rd_opcintype = 0x2a055b8,
rd_support = 0x2a055d8, rd_supportinfo = 0x2a05600, rd_indoption = 0x2a05738, rd_indexprs = 0x0, rd_indpred = 0x0,
rd_exclops = 0x0, rd_exclprocs = 0x0, rd_exclstrats = 0x0, rd_amcache = 0x0, rd_indcollation = 0x2a05718,
rd_fdwroutine = 0x0, rd_toastoid = 0, pgstat_info = 0x2a5e198}
(gdb) p *indrel->rd_rel
$9 = {relname = {data = "idx_t1_id", '\000' <repeats 54 times>}, relnamespace = 2200, reltype = 0, reloftype = 0,
relowner = 10, relam = 403, relfilenode = 50823, reltablespace = 0, relpages = 60, reltuples = 8901, relallvisible = 0,
reltoastrelid = 0, relhasindex = false, relisshared = false, relpersistence = 112 'p', relkind = 105 'i', relnatts = 1,
relchecks = 0, relhasoids = false, relhasrules = false, relhastriggers = false, relhassubclass = false,
relrowsecurity = false, relforcerowsecurity = false, relispopulated = true, relreplident = 110 'n',
relispartition = false, relrewrite = 0, relfrozenxid = 0, relminmxid = 0}
(gdb) p *stats
$7 = (IndexBulkDeleteResult *) 0x0
(gdb) p *vacrelstats
$8 = {hasindex = true, old_rel_pages = 124, rel_pages = 124, scanned_pages = 59, pinskipped_pages = 0,
frozenskipped_pages = 1, tupcount_pages = 59, old_live_tuples = 12686, new_rel_tuples = 14444, new_live_tuples = 14444,
new_dead_tuples = 0, pages_removed = 0, tuples_deleted = 100, nonempty_pages = 124, num_dead_tuples = 100,
max_dead_tuples = 36084, dead_tuples = 0x2ab8820, num_index_scans = 0, latestRemovedXid = 397076,
lock_waiter_detected = false}
(gdb)
初始化IndexVacuumInfo结构体
(gdb) n
1691 ivinfo.index = indrel;
(gdb)
1692 ivinfo.analyze_only = false;
(gdb)
1693 ivinfo.estimated_count = true;
(gdb)
1694 ivinfo.message_level = elevel;
(gdb)
1696 ivinfo.num_heap_tuples = vacrelstats->old_live_tuples;
(gdb)
1697 ivinfo.strategy = vac_strategy;
(gdb)
调用index_bulk_delete,进入该函数
1700 *stats = index_bulk_delete(&ivinfo, *stats,
(gdb) step
index_bulk_delete (info=0x7fff39c5d620, stats=0x0, callback=0x6bf507 <lazy_tid_reaped>, callback_state=0x2aaf958)
at indexam.c:748
748 Relation indexRelation = info->index;
(gdb)
输入参数
info -> IndexVacuumInfo结构体
stats为NULL
回调函数为lazy_tid_reaped
回调函数状态结构体为callback_state
(gdb) p *info
$10 = {index = 0x7f7334825050, analyze_only = false, estimated_count = true, message_level = 13, num_heap_tuples = 12686,
strategy = 0x2a9d478}
(gdb)
(gdb) p *callback_state
Attempt to dereference a generic pointer.
(gdb)
(gdb) p *info->strategy
$11 = {btype = BAS_VACUUM, ring_size = 32, current = 4, current_was_in_ring = false, buffers = 0x2a9d488}
(gdb)
调用indexRelation->rd_amroutine->ambulkdelete,该函数实际指向的是btbulkdelete
(gdb) n
750 RELATION_CHECKS;
(gdb)
751 CHECK_REL_PROCEDURE(ambulkdelete);
(gdb)
753 return indexRelation->rd_amroutine->ambulkdelete(info, stats,
(gdb) p indexRelation->rd_amroutine
$12 = (struct IndexAmRoutine *) 0x2a05480
(gdb) p *indexRelation->rd_amroutine
$13 = {type = T_IndexAmRoutine, amstrategies = 5, amsupport = 3, amcanorder = true, amcanorderbyop = false,
amcanbackward = true, amcanunique = true, amcanmulticol = true, amoptionalkey = true, amsearcharray = true,
amsearchnulls = true, amstorage = false, amclusterable = true, ampredlocks = true, amcanparallel = true,
amcaninclude = true, amkeytype = 0, ambuild = 0x5123f0 <btbuild>, ambuildempty = 0x507e6b <btbuildempty>,
aminsert = 0x507f11 <btinsert>, ambulkdelete = 0x5096b6 <btbulkdelete>, amvacuumcleanup = 0x509845 <btvacuumcleanup>,
amcanreturn = 0x50a21f <btcanreturn>, amcostestimate = 0x9c5356 <btcostestimate>, amoptions = 0x511cd4 <btoptions>,
amproperty = 0x511cfe <btproperty>, amvalidate = 0x51522b <btvalidate>, ambeginscan = 0x5082f7 <btbeginscan>,
amrescan = 0x508492 <btrescan>, amgettuple = 0x507f90 <btgettuple>, amgetbitmap = 0x50819e <btgetbitmap>,
amendscan = 0x508838 <btendscan>, ammarkpos = 0x508b28 <btmarkpos>, amrestrpos = 0x508d20 <btrestrpos>,
amestimateparallelscan = 0x5090e6 <btestimateparallelscan>, aminitparallelscan = 0x5090f1 <btinitparallelscan>,
amparallelrescan = 0x50913f <btparallelrescan>}
进入btbulkdelete
(gdb) step
btbulkdelete (info=0x7fff39c5d620, stats=0x0, callback=0x6bf507 <lazy_tid_reaped>, callback_state=0x2aaf958) at nbtree.c:857
857 Relation rel = info->index;
(gdb)
输入参数参见上述函数输入参数,类似
获取relation,初始化统计信息
857 Relation rel = info->index;
(gdb) n
861 if (stats == NULL)
(gdb)
862 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
(gdb)
866 PG_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
(gdb)
获取cycleid
(gdb) n
870 cycleid = _bt_start_vacuum(rel);
(gdb)
872 btvacuumscan(info, stats, callback, callback_state, cycleid,
(gdb) p cycleid
$14 = 1702
(gdb)
调用btvacuumscan,返回统计信息
(gdb) n
880 _bt_update_meta_cleanup_info(info->index, oldestBtpoXact,
(gdb)
883 PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
(gdb)
884 _bt_end_vacuum(rel);
(gdb)
886 return stats;
(gdb) p *stats
$15 = {num_pages = 60, pages_removed = 0, estimated_count = false, num_index_tuples = 8801, tuples_removed = 100,
pages_deleted = 6, pages_free = 6}
(gdb)
DONE!
btvacuumscan下节再行介绍
四、参考资料
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