本节继续介绍make_one_rel函数中的set_base_rel_pathlists->create_tidscan_paths函数,该函数创建相应的TID扫描路径。
一、数据结构
Cost相关
注意:实际使用的参数值通过系统配置文件定义,而不是这里的常量定义!
typedef double Cost;
#define DEFAULT_SEQ_PAGE_COST 1.0 //顺序扫描page的成本
#define DEFAULT_RANDOM_PAGE_COST 4.0 //随机扫描page的成本
#define DEFAULT_CPU_TUPLE_COST 0.01 //处理一个元组的CPU成本
#define DEFAULT_CPU_INDEX_TUPLE_COST 0.005 //处理一个索引元组的CPU成本
#define DEFAULT_CPU_OPERATOR_COST 0.0025 //执行一次操作或函数的CPU成本
#define DEFAULT_PARALLEL_TUPLE_COST 0.1 //并行执行,从一个worker传输一个元组到另一个worker的成本
#define DEFAULT_PARALLEL_SETUP_COST 1000.0 //构建并行执行环境的成本
#define DEFAULT_EFFECTIVE_CACHE_SIZE 524288
double seq_page_cost = DEFAULT_SEQ_PAGE_COST;
double random_page_cost = DEFAULT_RANDOM_PAGE_COST;
double cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
double cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
double cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
double parallel_tuple_cost = DEFAULT_PARALLEL_TUPLE_COST;
double parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;
int effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
Cost disable_cost = 1.0e10;//1后面10个0,通过设置一个巨大的成本,让优化器自动放弃此路径
int max_parallel_workers_per_gather = 2;//每次gather使用的worker数
二、源码解读
set_base_rel_pathlists->create_tidscan_paths函数创建相应的TID扫描路径。
void
create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
{
Relids required_outer;
List *tidquals;
required_outer = rel->lateral_relids;//需依赖的外部relids
tidquals = TidQualFromBaseRestrictinfo(rel);//tid条件子句
if (tidquals)
add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
required_outer));//添加tid路径(如有)
}
//-------------------------------------------------------------------------- TidQualFromBaseRestrictinfo
static List *
TidQualFromBaseRestrictinfo(RelOptInfo *rel)
{
List *rlst = NIL;
ListCell *l;
foreach(l, rel->baserestrictinfo)//循环遍历约束条件
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);//约束条件
if (!restriction_is_securely_promotable(rinfo, rel))
continue;
rlst = TidQualFromExpr((Node *) rinfo->clause, rel->relid);//获取结果链表
if (rlst)
break;//如有,则退出
}
return rlst;
}
//------------------------------------------------------ TidQualFromExpr
static List *
TidQualFromExpr(Node *expr, int varno)
{
List *rlst = NIL;
ListCell *l;
if (is_opclause(expr))//常规的表达式
{
if (IsTidEqualClause((OpExpr *) expr, varno))
rlst = list_make1(expr);
}
else if (expr && IsA(expr, ScalarArrayOpExpr))//ScalarArrayOpExpr
{
if (IsTidEqualAnyClause((ScalarArrayOpExpr *) expr, varno))
rlst = list_make1(expr);
}
else if (expr && IsA(expr, CurrentOfExpr))//CurrentOfExpr
{
if (((CurrentOfExpr *) expr)->cvarno == varno)
rlst = list_make1(expr);
}
else if (and_clause(expr))//AND
{
foreach(l, ((BoolExpr *) expr)->args)
{
rlst = TidQualFromExpr((Node *) lfirst(l), varno);
if (rlst)
break;
}
}
else if (or_clause(expr))//OR
{
foreach(l, ((BoolExpr *) expr)->args)
{
List *frtn = TidQualFromExpr((Node *) lfirst(l), varno);
if (frtn)
rlst = list_concat(rlst, frtn);
else
{
if (rlst)
list_free(rlst);
rlst = NIL;
break;
}
}
}
return rlst;
}
//-------------------------------------------------------------------------- create_tidscan_path
TidPath *
create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
Relids required_outer)
{
TidPath *pathnode = makeNode(TidPath);
pathnode->path.pathtype = T_TidScan;
pathnode->path.parent = rel;
pathnode->path.pathtarget = rel->reltarget;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->path.parallel_aware = false;
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = 0;
pathnode->path.pathkeys = NIL;
pathnode->tidquals = tidquals;
cost_tidscan(&pathnode->path, root, rel, tidquals,
pathnode->path.param_info);//计算成本
return pathnode;
}
//-------------------------------------------------------- cost_tidscan
void
cost_tidscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, List *tidquals, ParamPathInfo *param_info)
{
Cost startup_cost = 0;
Cost run_cost = 0;
bool isCurrentOf = false;
QualCost qpqual_cost;
Cost cpu_per_tuple;
QualCost tid_qual_cost;
int ntuples;
ListCell *l;
double spc_random_page_cost;
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
if (param_info)
path->rows = param_info->ppi_rows;
else
path->rows = baserel->rows;//行数
ntuples = 0;
foreach(l, tidquals)//遍历条件表达式
{
if (IsA(lfirst(l), ScalarArrayOpExpr))//ScalarArrayOpExpr
{
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) lfirst(l);
Node *arraynode = (Node *) lsecond(saop->args);
ntuples += estimate_array_length(arraynode);
}
else if (IsA(lfirst(l), CurrentOfExpr))//CurrentOfExpr
{
isCurrentOf = true;
ntuples++;
}
else
{
ntuples++;//计数+1
}
}
if (isCurrentOf)//CurrentOfExpr
{
Assert(baserel->baserestrictcost.startup >= disable_cost);
startup_cost -= disable_cost;
}
else if (!enable_tidscan)//如禁用tidscan
startup_cost += disable_cost;//设置为高成本
cost_qual_eval(&tid_qual_cost, tidquals, root);
get_tablespace_page_costs(baserel->reltablespace,
&spc_random_page_cost,
NULL);//表空间page访问成本
run_cost += spc_random_page_cost * ntuples;//运行成本
get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);//CPU扫描成本
startup_cost += qpqual_cost.startup + tid_qual_cost.per_tuple;
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple -
tid_qual_cost.per_tuple;
run_cost += cpu_per_tuple * ntuples;
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
三、跟踪分析
测试脚本如下
select a.ctid,a.dwbh,a.dwmc,b.grbh,b.xm,b.xb,b.nl
from t_dwxx a,t_grxx b
where a.ctid = '(2,10)'::tid
and a.dwbh = b.dwbh;
启动gdb,设置断点
(gdb) b create_tidscan_paths
Breakpoint 2 at 0x759b06: file tidpath.c, line 263.
(gdb) c
Continuing.
Breakpoint 2, create_tidscan_paths (root=0x2869588, rel=0x2869998) at tidpath.c:263
263 required_outer = rel->lateral_relids;
进入create_tidscan_paths->TidQualFromBaseRestrictinfo函数
(gdb) n
265 tidquals = TidQualFromBaseRestrictinfo(rel);
(gdb) step
TidQualFromBaseRestrictinfo (rel=0x2869998) at tidpath.c:225
225 List *rlst = NIL;
获取TID条件表达式,对应的是:a.ctid = '(2,10)'::tid
...
(gdb) p *(Var *)$tmp->args->head->data.ptr_value
$11 = {xpr = {type = T_Var}, varno = 1, varattno = -1, vartype = 27, vartypmod = -1, varcollid = 0, varlevelsup = 0,
varnoold = 1, varoattno = -1, location = 81}
(gdb) p *(Const *)$tmp->args->head->next->data.ptr_value
$12 = {xpr = {type = T_Const}, consttype = 27, consttypmod = -1, constcollid = 0, constlen = 6, constvalue = 41705832,
constisnull = false, constbyval = false, location = 90}
进入create_tidscan_path函数
(gdb)
create_tidscan_paths (root=0x2869588, rel=0x2869998) at tidpath.c:267
267 if (tidquals)
(gdb) n
268 add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
(gdb) step
create_tidscan_path (root=0x2869588, rel=0x2869998, tidquals=0x287ef90, required_outer=0x0) at pathnode.c:1191
1191 TidPath *pathnode = makeNode(TidPath);
进入cost_tidscan
(gdb) step
cost_tidscan (path=0x287eee0, root=0x2869588, baserel=0x2869998, tidquals=0x287ef90, param_info=0x0) at costsize.c:1184
1184 Cost startup_cost = 0;
#解析表达式的CPU成本
(gdb)
1249 cost_qual_eval(&tid_qual_cost, tidquals, root);
(gdb)
1252 get_tablespace_page_costs(baserel->reltablespace,
(gdb) p tid_qual_cost
$14 = {startup = 0, per_tuple = 0.0025000000000000001}
计算完毕,返回结果
...
(gdb)
1272 path->startup_cost = startup_cost;
(gdb)
1273 path->total_cost = startup_cost + run_cost;
(gdb)
1274 }
(gdb)
(gdb) p *path
$17 = {type = T_TidPath, pathtype = T_TidScan, parent = 0x2869998, pathtarget = 0x287ac38, param_info = 0x0,
parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 1, startup_cost = 0.0025000000000000001,
total_cost = 4.0125000000000002, pathkeys = 0x0}
结束create_tidscan_paths函数调用
(gdb) n
create_tidscan_path (root=0x2869588, rel=0x2869998, tidquals=0x287ef90, required_outer=0x0) at pathnode.c:1208
1208 return pathnode;
(gdb)
1209 }
(gdb)
create_tidscan_paths (root=0x2869588, rel=0x2869998) at tidpath.c:270
270 }
(gdb)
set_plain_rel_pathlist (root=0x2869588, rel=0x2869998, rte=0x27c5318) at allpaths.c:718
718 }
四、参考资料
allpaths.c
cost.h
costsize.c
PG Document:Query Planning
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/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
相关文章
发现更多好内容- Java中dubbo的最佳实践案例有哪些?(java中dubbo有哪些最佳实践案例)
- 如何通过 Java 代码获取当前时间戳?(如何使用Java获取当前时间戳)
- 如何使用 Java 编写一个 circular 类?(怎么用java编写一个circular类)
- 在 Java 中如何安全地进行 SQL 拼接?(Java中怎么安全的进行SQL拼接)
- Java 的 enum 有哪些需要注意的事项?(java的enum的注意事项)
- 深入解析:如何有效提升PHP数据类型的转换效率
- 如何在 Java 中实现轮询?(java如何实现轮询)
- 如何在 Java 中获取当前时间戳?(java怎么获取当前时间戳)
- 如何简化 Java 的 DAO 层开发流程?(Java的DAO层怎样简化开发流程)
- 深入解析PHP中数据类型的转换技巧