Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

62.6. 索引成本估算函数 #

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

在成本估算中应考虑的索引扫描重复次数。在考虑在嵌套循环连接内侧使用参数化扫描时,通常会大于 1。请注意,成本估算仍应该针对一次扫描;较大的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 成本,但包括由索引标识的父表行的检索或处理成本。

启动成本是我们开始获取第一行之前必须耗用的全部扫描成本的一部分。对于大多数索引可以将其视为 0,但具有较高启动成本的索引类型可能希望将其设置为非零。

indexSelectivity 应设定为索引扫描期间将检索的父表行估计量。对于低精度查询,这通常会高于实际上通过给定 qual 条件的行比例。

indexCorrelation 应设定为索引顺序与表顺序之间的相关性(介于 -1.0 和 1.0 之间)。这用于调整从父表获取行的成本估算。

indexPages 应设定为叶页数。这用于估计并行索引扫描的工作线程数。

loop_count 大于一时,应返回预期用于索引任意一次扫描的平均数。

成本估算

典型的成本估算器将按如下方式进行

  1. 基于给定的 qual 条件,估算并返回将访问的父表行的比例。在没有任何索引类型特定知识的情况下,使用标准优化器函数 clauselist_selectivity()

    *indexSelectivity = clauselist_selectivity(root, path->indexquals,
                                               path->indexinfo->rel->relid,
                                               JOIN_INNER, NULL);
    
  2. 估算扫描期间将访问的索引行数。对于许多索引类型,这与 indexSelectivity 乘以索引中的行数相同,但可能更大。(注意,可以从 path->indexinfo 结构获得索引在页面和行中的大小。)

  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, path->indexquals, root);
    *indexStartupCost = index_qual_cost.startup;
    *indexTotalCost = seq_page_cost * numIndexPages +
        (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
    

    不过,上面没有考虑在重复索引扫描中分摊索引读取。

  5. 估算索引相关性。对于在单个字段上的简单有序索引,这可以通过 pg_statistic 检索。如果不知道相关性,则保守估计为零(无相关性)。

可以在 src/backend/utils/adt/selfuncs.c 中找到成本估算器函数的示例。