函数 amcostestimate
接收关于一个可能的索引扫描的信息,包括已确定可用于该索引的 WHERE 和 ORDER BY 子句列表。它必须返回对访问索引的成本以及 WHERE 子句的选择性(即,在索引扫描期间将检索的父表行的比例)的估算。对于简单情况,成本估算器的大部分工作可以通过调用优化器中的标准例程来完成;拥有一个 amcostestimate
函数的目的在于允许索引访问方法提供特定于索引类型的知识,以便有可能改进标准估算。
每个 amcostestimate
函数都必须具有签名
void amcostestimate (PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages);
前三个参数是输入
root
优化器关于正在处理的查询的信息。
path
正在考虑的索引访问路径。除成本和选择性值外的所有字段都有效。
loop_count
应该计入成本估算中的索引扫描重复次数。在考虑用于嵌套循环连接内部的参数化扫描时,此值通常会大于一。请注意,成本估算仍应只针对一次扫描;较大的 loop_count
意味着可能允许在多次扫描之间进行一些缓存效果。
最后五个参数是按引用传递的输出
*indexStartupCost
设置为索引启动处理的成本
*indexTotalCost
设置为索引处理的总成本
*indexSelectivity
设置为索引选择性
*indexCorrelation
设置为索引扫描顺序与底层表的顺序之间的相关系数
*indexPages
设置为索引叶子页的数量
请注意,成本估算函数必须用 C 编写,而不是用 SQL 或任何可用的过程语言编写,因为它们必须访问优化器/规划器的内部数据结构。
索引访问成本应使用 src/backend/optimizer/path/costsize.c
使用的参数进行计算:顺序磁盘块读取成本为 seq_page_cost
,非顺序读取成本为 random_page_cost
,处理一个索引行的成本通常应视为 cpu_index_tuple_cost
。此外,对于在索引处理期间(尤其是索引本身的求值)调用的任何比较运算符,都应收取 cpu_operator_cost
的适当倍数。
访问成本应包括扫描索引本身的所有磁盘和 CPU 成本,但不包括检索或处理由索引标识的父表行的成本。
“启动成本” 是总扫描成本中在能够开始获取第一行之前必须花费的部分。对于大多数索引,这可以视为零,但启动成本较高的索引类型可能希望将其设置为非零。
indexSelectivity
应设置为在索引扫描期间将检索的父表行的估算比例。在有损查询的情况下,这通常会高于实际通过给定条件的选择的行比例。
indexCorrelation
应设置为索引顺序与表顺序之间的相关性(范围在 -1.0 到 1.0 之间)。这用于调整从父表获取行的成本估算。
indexPages
应设置为叶子页的数量。这用于估算并行索引扫描的工作进程数。
当 loop_count
大于一倍时,返回的数字应是对于对索引的任何一次扫描的预期平均值。
成本估算
典型的成本估算器将按以下方式进行:
根据给定的条件估算并返回将访问的父表行的比例。在没有任何特定于索引类型的知识的情况下,使用标准优化器函数 clauselist_selectivity()
*indexSelectivity = clauselist_selectivity(root, path->indexquals, path->indexinfo->rel->relid, JOIN_INNER, NULL);
估算扫描期间将访问的索引行数。对于许多索引类型,这等于 indexSelectivity
乘以索引中的行数,但可能会更多。(请注意,索引的页面和行大小可从 path->indexinfo
结构中获得。)
估算扫描期间将读取的索引页数。这可能只是 indexSelectivity
乘以索引的页面大小。
计算索引访问成本。一个通用的估算器可能会这样做:
/* * 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, path->indexquals, root); *indexStartupCost = index_qual_cost.startup; *indexTotalCost = seq_page_cost * numIndexPages + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
但是,上述方法没有考虑索引读取在重复索引扫描之间的摊销。
估算索引相关性。对于基于单个字段的简单有序索引,可以从 pg_statistic 中检索。如果相关性未知,则保守估算为零(无相关性)。
成本估算函数的示例可以在 src/backend/utils/adt/selfuncs.c
中找到。
如果您在文档中发现任何不正确之处,与您在使用特定功能时的经验不符,或者需要进一步澄清,请使用 此表格 报告文档问题。