↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

不受支持的版本: 8.0 / 7.4 / 7.3 / 7.2 / 7.1
历史版本PostgreSQL 8.0 已于 2010 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本手册首页。

第 47 章 索引代价估算函数

作者

由 Tom Lane()于 2000-01-24 撰写

注意

这些内容最终应当成为关于编写新索引访问方法的更大篇幅章节的一部分。

每种索引访问方法都必须提供一个供规划器/优化器使用的代价估算函数。 该函数的过程 OID 由该访问方法的 pg_am 项中的 amcostestimate 字段给出。

注意

在 PostgreSQL 7.0 之前,注册索引相关的 代价估算函数使用的是另一种方案。

amcostestimate 函数会得到一个已被确定可用于该索引的 WHERE 子句列表。它必须返回访问该索引的代价估计,以及 WHERE 子句 的选择率估计(即在索引扫描期间将被检索的主表行所占的比例)。 对于简单情况,代价估算器几乎全部工作都可以通过调用优化器中的 标准例程来完成;之所以要提供 amcostestimate 函数,是为了让索引访问方法能够提供与 索引类型相关的知识,以便在可能的情况下改进标准估计。

每个 amcostestimate 函数都必须具有下面的签名:

void
amcostestimate (Query *root,
                RelOptInfo *rel,
                IndexOptInfo *index,
                List *indexQuals,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation);
   

前四个参数是输入:

root

正在处理的查询。

rel

索引所在的关系。

index

索引本身。

indexQuals

索引限定子句的列表(隐含地做 AND 连接); NIL 列表表示没有可用的限定条件。

最后四个参数是按引用传递的输出参数:

*indexStartupCost

设为索引启动处理的代价

*indexTotalCost

设为索引处理的总代价

*indexSelectivity

设为索引选择率

*indexCorrelation

设为索引扫描顺序与底层表顺序之间的相关系数

请注意,代价估算函数必须用 C 编写,而不能用 SQL 或任何可用的过程语言,因为它们必须访问规划器/优化器的内部数据结构。

索引访问代价应使用 src/backend/optimizer/path/costsize.c 所用的单位 来计算:顺序磁盘块访问的代价为 1.0,非顺序访问的代价为 random_page_cost,而处理一个索引行的代价 通常取为 cpu_index_tuple_cost(这是一个用户可调的 优化器参数)。此外,对于索引处理(尤其是 indexQuals 本身的求值)期间调用的每个比较操作符,都应计入 cpu_operator_cost 的适当倍数。

访问代价应包括与扫描索引本身有关的全部磁盘和 CPU 代价,但不包括取出或处理由索引标识出的主表行的代价。

“启动代价”是整个扫描总代价中必须在开始取第一行之前先付出的那一部分。对大多数索引来说,这可以视为零;但启动代价较高的索引类型可能希望把它设为非零。

indexSelectivity 应设为在索引扫描期间将从主表中检索出的行的估计比例。对于有损索引,这个值通常会高于实际通过给定限定条件的行比例。

indexCorrelation 应设为索引顺序与表顺序之间的相关性(范围从 -1.0 到 1.0)。该值用于调整从主表取行代价的估计。

代价估算

一个典型的代价估算器会按如下步骤进行:

  1. 根据给定的限定条件,估计并返回将被访问的主表行所占的比例。 在没有索引类型相关知识的情况下,使用标准优化器函数 clauselist_selectivity():

    *indexSelectivity = clauselist_selectivity(root, indexQuals,
                                               rel->relid, JOIN_INNER);
         
    
  2. 估计扫描期间将被访问的索引行数。对于许多索引类型,它等于 indexSelectivity 乘以索引中的行数,但也可能更多。 (注意,索引的大小(页数和行数)可以从 IndexOptInfo 结构体中获得。)

  3. 估计扫描期间将读取的索引页面数。它可能仅仅是 indexSelectivity 乘以索引总页面数。

  4. 计算索引访问代价。一个通用估计器可能会这样做:

        /*
         * Our generic assumption is that the index pages will be read
         * sequentially, so they have cost 1.0 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);
        *indexStartupCost = index_qual_cost.startup;
        *indexTotalCost = numIndexPages +
            (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
         
    
  5. 估计索引的相关性。对于单列上的简单有序索引,这个值可以从 pg_statistic 中取得。如果相关性未知,保守估计应为零(即无相关性)。

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

按照惯例,amcostestimate 函数的 pg_proc 项应当显示八个全部声明为 internal 的参数(因为它们的类型都不为 SQL 所知),并且返回类型为 void。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。