1.1.1 两表连接的代价估算
1.1.1.1 归并连接的代价:final_cost_mergejoin
函数功能:
计算两个关系使用归并连接算法的花费。有些操作被缓存便于后续使用。内外表的可连接[1]的元组数决定着CPU的花费。
函数功能:
计算两个关系使用归并连接连接算法的花费。
对于mergejoin花费的计算,分为两个函数:
1. 一是initial_cost_mergejoin函数,初步估计mergejoin算法的花费,形成的结果会作为final_cost_mergejoin的入口参数(JoinCostWorkspace *workspace)传入final_cost_mergehjoin函数;
2. 二是final_cost_mergejoin函数,对mergejoin算法的花费进行计算,并决定是否选用物化操作优化mergejoin连接。
代码分析:
void
final_cost_mergejoin(PlannerInfo *root, MergePath *path,
JoinCostWorkspace *workspace,
SpecialJoinInfo *sjinfo)
{......
//计算mergequals、qpquals(other restriction clauses)的花费(mergequals是joinrestrictinfo的子部分)
cost_qual_ev
cost_qual_ev
qp_qual_cost.startup -= merge_qual_cost.startup;
qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple;
//获取mergequals子句涉及的元组数,计算CPU花费的重要依据
//每个子句都有不同的选择率(clause_selectivity函数计算子句的选择率)
//approx_tuple_count函数计算归并子句(mergeclauses)中的各子句的选择率,这些选择率的积就是归并条件的总的选择率,总的选择率乘以内关系的元组数再乘以外关系的元组数,由此可以得到归并后的总的元组数(mergejointuples),将来要依据mergejointuples得出这些元组归并连接时的CPU花费
mergejointuples = approx_tuple_count(root, &path->jpath, mergeclauses);
//mergejoin算法,读入外表的一条元组,然后与内表的每个元组比较;如果外表有相等的元组,则内表需要多次读入,所以,重新扫描率总可能比1大
//如果外表键值是唯一的,则不存在内表需要重复读入的问题
if (IsA(outer_path, UniquePath))
rescannedtuples = 0;
else//否则,要考虑内表需要重复读入
{
rescannedtuples = mergejointuples - inner_path_rows;
if (rescannedtuples < 0)
rescannedtuples = 0;
}
rescanratio = 1.0 + (rescannedtuples / inner_path_rows);
//计算内表在考虑了重复读入的问题后的全部花费
bare_inner_cost = inner_run_cost * rescanratio;
//物化内表的花费
mat_inner_cost = inner_run_cost +
cpu_operator_cost * inner_path_rows * rescanratio;
//内表物化比内表重读花费小,则选择物化方式的优化
if (enable_material && mat_inner_cost < bare_inner_cost)
path->materialize_inner = true;
else if (innersortkeys == NIL &&//内表无序且不支持mark/restore,选用物化
!ExecSupportsMarkRestore(inner_path->pathtype))
path->materialize_inner = true;
else if (enable_material && innersortkeys != NIL &&//内表有序但排序空间不够,选用物化
relation_byte_size(inner_path_rows, inner_path->parent->width) >
(work_mem * 1024L))
path->materialize_inner = true;
else
path->materialize_inner = false;
if (path->materialize_inner)
run_cost += mat_inner_cost;
else
run_cost += bare_inner_cost;
//知道了选择率、元组数等,则可以计算CPU花费:
//1 归并的CPU的花费---进行比较的花费
startup_cost += merge_qual_cost.startup;
startup_cost += merge_qual_cost.per_tuple *
(outer_skip_rows + inner_skip_rows * rescanratio);
run_cost += merge_qual_cost.per_tuple * //进行比较的花费
((outer_rows - outer_skip_rows) +
(inner_rows - inner_skip_rows) * rescanratio);
//2 归并的CPU的花费—进行连接的花费
startup_cost += qp_qual_cost.startup;
cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;
run_cost += cpu_per_tuple * mergejointuples; //进行连接的花费
path->jpath.path.startup_cost = startup_cost;
path->jpath.path.total_cost = startup_cost + run_cost;
}
调用关系图解:
1. final_cost_mergejoin函数被create_mergejoin_path函数调用,直至被add_paths_to_joinrel函数调用,完成两表连接的mergejoin连接算法的花费估算;
2. final_cost_mergejoin函数被sort_inner_and_outer、match_unsorted_outer这两个函数分别调用,用以应对多种情况(内外表是否有序)的连接操作。
本文介绍数据库中两表使用归并连接的成本估算过程,包括初步估计与最终计算两个阶段,通过分析内外表元组数量及可连接性确定CPU成本,并探讨了物化操作优化的条件。
2128

被折叠的 条评论
为什么被折叠?



