©
This document uses PHP Chinese website manual Release
系统给 amcostestimate
函数一个WHERE子句的列表,这个 WHERE 子
句列表是系统认为可以被索引使用的东西。它必须返回访问该索引的开销估计值以
及 WHERE 子句的选择性(也就是说,在索引扫描期间检索的将被返回的数据行在父
表中所占据的比例)。对于简单的场合,几乎开销估计器的所有工作都可以通过调
用优化器里面的标准过程完成;有amcostestimate
这个函数的目的
是允许索引访问方法提供和索引类型相关的知识,这样也许可以改进标准的开销估计。
每个 amcostestimate
函数都有下面这样的签名:
void amcostestimate (PlannerInfo *root, IndexOptInfo *index, List *indexQuals, RelOptInfo *outer_rel, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation);
头四个参数是输入:
规划器的有关正在被处理的查询的信息。
在考虑使用的索引。
索引条件子句的列表(隐含是AND的);如果是NIL列表 (空列表)就表示没有可用的条件。请注意这个列表包含表达式树,而不是 ScanKey(扫描键字)。
如果该索引可能要用于连接内部扫描,那么这个将是规划器关于连接 的外侧信息,否则为NULL。当不为NULL时, 一些qual字句将会连接使用带有这个rel的字句而不是简单的约束字句。 同样,开销评估应当考虑到索引扫描将会为rel的每一行执行一次。
后面四个参数是传递引用的输出:
设置为索引启动处理的开销
设置为索引处理的总开销
设置为索引的选择型
设置为索引扫描顺序和下层的表的顺序之间的相关有效性
请注意开销估计函数必须用C写,而不能用SQL或者任何可用的存储过程 语言,因为它们必须访问规划器/优化器的内部数据结构。
索引访问开销应该以 src/backend/optimizer/path/costsize.c:使用的单位进行计算:一个顺序磁盘块抓取开销是1.0 , 一个非顺序抓取开销是seq_page_cost ,而处理一个索引行的 开销通常应该是random_page_cost,而处理一个索引行的开销 通常应该是cpu_index_tuple_cost。另外,在任何索引处理期 间调用的比较操作符,都应该增加一个数量为cpu_operator_cost倍数的开销(特别是计算索引条件 indexQuals自己的时候)。
访问开销应该包括所有与扫描索引本身相关的磁盘和CPU开销,但是不包括检 索或者处理索引标识出来的父表的行的开销。
"启动开销""start-up cost"是总扫描开销中的这样一部分: 在开始抓取第一行之前,必须花掉的开销。对于大多数索引,这个可以是零,但 是那些启动开销很大的索引类型可能不能把它设置为零。
indexSelectivity 应该设置成在索引扫描期间,父表中的行被 选出出来的部分的百分比。在索引比较松散的情况下,这个值通常比实际通过 给出的查询条件之行所占的百分比要高。
indexCorrelation应该设置成索引顺序和表顺序之间的相关性 (范围在 -1.0 到 1.0 之间)。这个数值用于调整从父表中抓取行的开销估计。
在连接情况下,返回的数值应当在每一次索引扫描之间平均。
开销估计
一个典型的开销估计器会像下面这样进行处理:
1.基于给出的查询条件,估计并返回父表中将被访问的行的百分比。如果缺
乏索引类型相关得知识,那么使用标准的优化器函数 clauselist_selectivity()
:
*indexSelectivity = clauselist_selectivity(root, indexQuals, index->rel->relid, JOIN_INNER, NULL);
2.估计在扫描过程中将被访问的索引行数。对于许多索引类型,这个等于 indexSelectivity乘以索引中的行数,但是可能更多。 请注意,页面中的索引尺寸和行数可以从IndexOptInfo 结构中获得。
3.估计在扫描中将检索的索引页面数量。这个可能就是 indexSelectivity乘以索引的总页面数。
4.计算索引访问开销。一个通用的估计器可以这么干:
/* * Our generic assumption is that the index pages will be read * sequentially, so they cost seq_page_cost each, not random_page_cost. * Also, we charge for evaluation of the indexquals at each index row. * All the costs are assumed to be paid incrementally during the scan. */ cost_qual_eval(&index_qual_cost, indexQuals, root); *indexStartupCost = index_qual_cost.startup; *indexTotalCost = seq_page_cost * numIndexPages + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
不过,上面没有考虑在连接情况下的多次索引扫描中分期(amortization)开销。
Estimate the index correlation. For a simple ordered index on a single field, this can be retrieved from pg_statistic. If the correlation is not known, the conservative estimate is zero (no correlation).
5.估计索引的相关性。对于简单的在单个字段上的有序索引,这个值可以从 pg_statistic中检索。如果相关性是未知,那么保守的估计是零(没有相关性)。
开销估计器函数的例子可以在src/backend/utils/adt/ selfuncs.c 里面找到。