本篇内容主要讲解“PostgreSQL中vacuum过程分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL中vacuum过程分析”吧!
一、数据结构
宏定义
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是VACUUM/ANALYZE命令的内部处理入口.
逻辑比较简单:
1.配置vacuum处理的相关参数,如命令类型等
2.执行相关检查
3.构造vacuum处理上下文
4.构造vacuum需处理的relation链表
5.循环遍历relation链表
5.1 获取relation
5.2 执行vacuum_rel
6.收尾工作
void
vacuum(int options, List *relations, VacuumParams *params,
BufferAccessStrategy bstrategy, bool isTopLevel)
{
static bool in_vacuum = false;//是否在vacuum
const char *stmttype;//语句类型,vacuum?analyze?
volatile bool in_outer_xact,
use_own_xacts;
Assert(params != NULL);
stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
if (options & VACOPT_VACUUM)
{
PreventInTransactionBlock(isTopLevel, stmttype);
in_outer_xact = false;
}
else
in_outer_xact = IsInTransactionBlock(isTopLevel);
if (in_vacuum)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("%s cannot be executed from VACUUM or ANALYZE",
stmttype)));
if ((options & VACOPT_FULL) != 0 &&
(options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
pgstat_vacuum_stat();
vac_context = AllocSetContextCreate(PortalContext,
"Vacuum",
ALLOCSET_DEFAULT_SIZES);
if (bstrategy == NULL)
{
MemoryContext old_context = MemoryContextSwitchTo(vac_context);
bstrategy = GetAccessStrategy(BAS_VACUUM);
MemoryContextSwitchTo(old_context);
}
vac_strategy = bstrategy;
if (relations != NIL)
{
List *newrels = NIL;
ListCell *lc;
foreach(lc, relations)
{
VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
List *sublist;
MemoryContext old_context;
sublist = expand_vacuum_rel(vrel, options);
old_context = MemoryContextSwitchTo(vac_context);
newrels = list_concat(newrels, sublist);
MemoryContextSwitchTo(old_context);
}
relations = newrels;
}
else
relations = get_all_vacuum_rels(options);
if (options & VACOPT_VACUUM)
use_own_xacts = true;
else
{
Assert(options & VACOPT_ANALYZE);
if (IsAutoVacuumWorkerProcess())
use_own_xacts = true;
else if (in_outer_xact)
use_own_xacts = false;
else if (list_length(relations) > 1)
use_own_xacts = true;
else
use_own_xacts = false;
}
if (use_own_xacts)
{
Assert(!in_outer_xact);
//autovacuum不会设置ActiveSnapshot
if (ActiveSnapshotSet())
PopActiveSnapshot();
//匹配PostgresMain()中的StartTransaction
CommitTransactionCommand();
}
//设置vacuum成本计数on/off,并set/clear in_vacuum参数
PG_TRY();
{
ListCell *cur;
in_vacuum = true;
VacuumCostActive = (VacuumCostDelay > 0);
VacuumCostBalance = 0;
VacuumPageHit = 0;
VacuumPageMiss = 0;
VacuumPageDirty = 0;
foreach(cur, relations)
{
VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
if (options & VACOPT_VACUUM)
{
//执行vacuum处理
if (!vacuum_rel(vrel->oid, vrel->relation, options, params))
continue;
}
if (options & VACOPT_ANALYZE)
{
if (use_own_xacts)
{
//使用自己的事务
StartTransactionCommand();
//快照压栈
PushActiveSnapshot(GetTransactionSnapshot());
}
//分析relation
analyze_rel(vrel->oid, vrel->relation, options, params,
vrel->va_cols, in_outer_xact, vac_strategy);
if (use_own_xacts)
{
//使用自己的事务,出栈
PopActiveSnapshot();
//提交事务
CommitTransactionCommand();
}
}
}
}
PG_CATCH();
{
in_vacuum = false;
VacuumCostActive = false;
PG_RE_THROW();
}
PG_END_TRY();
in_vacuum = false;
VacuumCostActive = false;
if (use_own_xacts)
{
//在这里,没有处于事务中
StartTransactionCommand();
}
if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
{
vac_update_datfrozenxid();
}
MemoryContextDelete(vac_context);
vac_context = NULL;
}
三、跟踪分析
测试脚本
17:19:28 (xdb@[local]:5432)testdb=# vacuum t1;
启动gdb,设置断点
(gdb) b vacuum
Breakpoint 1 at 0x6b9b8c: file vacuum.c, line 175.
(gdb) c
Continuing.
Breakpoint 1, vacuum (options=1, relations=0x2294988, params=0x7fff403d8880, bstrategy=0x0, isTopLevel=true) at vacuum.c:175
175 Assert(params != NULL);
(gdb)
输入参数
options=1 —> VACOPT_VACUUM
relations=0x2294988,relation链表,里面只有一个item,即t1
params=0x7fff403d8880,默认参数
bstrategy=NULL,
isTopLevel=T,为顶层事务
(gdb) p *params
$2 = {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)
变量赋值并执行相关判断
(gdb) n
177 stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
(gdb)
187 if (options & VACOPT_VACUUM)
(gdb)
189 PreventInTransactionBlock(isTopLevel, stmttype);
(gdb)
190 in_outer_xact = false;
(gdb)
200 if (in_vacuum)
(gdb)
209 if ((options & VACOPT_FULL) != 0 &&
(gdb)
统计信息
219 if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
(gdb)
220 pgstat_vacuum_stat();
(gdb)
创建并设置内存上下文
(gdb) n
228 vac_context = AllocSetContextCreate(PortalContext,
(gdb)
236 if (bstrategy == NULL)
(gdb)
238 MemoryContext old_context = MemoryContextSwitchTo(vac_context);
(gdb)
240 bstrategy = GetAccessStrategy(BAS_VACUUM);
(gdb)
241 MemoryContextSwitchTo(old_context);
(gdb)
243 vac_strategy = bstrategy;
(gdb)
249 if (relations != NIL)
(gdb)
构造VacuumRelation链表
(gdb)
251 List *newrels = NIL;
(gdb)
254 foreach(lc, relations)
(gdb)
256 VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
(gdb)
260 sublist = expand_vacuum_rel(vrel);
(gdb) p *vrel
$3 = {type = T_VacuumRelation, relation = 0x22948d0, oid = 0, va_cols = 0x0}
(gdb) p *vrel->relation
$4 = {type = T_RangeVar, catalogname = 0x0, schemaname = 0x0, relname = 0x22948b0 "t1", inh = true,
relpersistence = 112 'p', alias = 0x0, location = 7}
(gdb)
(gdb) n
261 old_context = MemoryContextSwitchTo(vac_context);
(gdb)
262 newrels = list_concat(newrels, sublist);
(gdb)
263 MemoryContextSwitchTo(old_context);
(gdb)
254 foreach(lc, relations)
(gdb)
265 relations = newrels;
(gdb)
使用自主事务
284 if (options & VACOPT_VACUUM)
(gdb)
285 use_own_xacts = true;
(gdb)
307 if (use_own_xacts)
(gdb)
307 if (use_own_xacts)
(gdb)
309 Assert(!in_outer_xact);
(gdb)
312 if (ActiveSnapshotSet())
(gdb)
313 PopActiveSnapshot();
(gdb)
316 CommitTransactionCommand();
(gdb)
320 PG_TRY();
(gdb)
开始执行,设置vacuum成本计数on/off,并set/clear in_vacuum参数
(gdb)
324 in_vacuum = true;
(gdb)
325 VacuumCostActive = (VacuumCostDelay > 0);
(gdb)
326 VacuumCostBalance = 0;
(gdb)
327 VacuumPageHit = 0;
(gdb)
328 VacuumPageMiss = 0;
(gdb)
329 VacuumPageDirty = 0;
(gdb)
循环relation,调用vacuum_rel
334 foreach(cur, relations)
(gdb)
336 VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
(gdb)
338 if (options & VACOPT_VACUUM)
(gdb)
340 if (!vacuum_rel(vrel->oid, vrel->relation, options, params))
(gdb)
344 if (options & VACOPT_ANALYZE)
(gdb)
334 foreach(cur, relations)
(gdb)
374 PG_END_TRY();
(gdb)
执行收尾工作
(gdb)
376 in_vacuum = false;
(gdb)
377 VacuumCostActive = false;
(gdb)
382 if (use_own_xacts)
(gdb)
390 StartTransactionCommand();
(gdb)
393 if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
(gdb)
399 vac_update_datfrozenxid();
(gdb)
407 MemoryContextDelete(vac_context);
(gdb)
408 vac_context = NULL;
(gdb)
完成调用
409 }
(gdb)
ExecVacuum (vacstmt=0x22949c0, isTopLevel=true) at vacuum.c:142
142 }
(gdb)
到此,相信大家对“PostgreSQL中vacuum过程分析”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!