第四十四章. 索引开销计算函数

作者:由 Tom Lane 写于 2000-01-24.
注意:这些内容最后必须成为关于书写新的索引访问方式的更大的一章的一部分.
每种索引访问模式都必须提供一个用于规划器/优化器的开销计算函数.这个函数的过程 OID 在访问模式在 pg_am 里的记录的 amcostestimate 字段里给出.
注意:在 Postgres 7.0 以前,使用的是另外一种注册与索引相关的开销计算函数的构造.
amcostestimate 函数收到一列 WHERE 子句,这些子句被认为是对索引有用的.这个函数本身必须返回计算出来的访问索引的开销和 WHERE 子句的选择性(也就是说,在索引扫描过程中主表中要被捡索出来的部分).对于简单的情况,几乎所有开销计算器的工作都可以通过调用优化器里标准的过程来完成,需要一个 amcostestimate 函数的原因是允许索引访问模式提供一些索引类型相关的信息,这样就有可能改进标准的计算(预计).

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

void
amcostestimate (Query *root,
                RelOptInfo *rel,
                IndexOptInfo *index,
                List *indexQuals,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity);
前面四个参数是输入:
root
被处理的查询.
rel
索引所处的关系.
index
索引本身.
indexQuals
索引条件子句列表(隐含地 AND);一个 NIL 列表表明没有可用的条件.
最后三个参数是传递的引用输出:
*indexStartupCost
设置为索引启动处理的开销
*indexTotalCost
设置为索引处理的总开销
*indexSelectivity
设置为索引选择性
请注意开销计算函数必须用 C 写,因为他们必须访问规划器/优化器的内部数据结构.

索引访问开销应该以 src/backend/optimizer/path/costsize.c 里面使用的单位计算:一次顺序磁盘存储块抓取开销为 1.0,一次非顺序抓取的开销为 random_page_cost,并且处理一个索引记录的开销通常应该当做 cpu_index_tuple_cost (它是一个可以由用户调节的优化器参数).另外,应该用一个cpu_operator_cost 的合适的倍数作为索引处理期间任何激活的比较操作符(尤其是计算 indexQuals (索引查询)自己).

访问开销应该包含所有与扫描索引本身的相关的磁盘和 CPU 开销,而不是检索或处理被索引标识的主表索引的开销.

"启动开销"是全部索引开销中在我们开始抓取第一条记录之前必须消耗的开销.对于大多数索引,这部分可以当做零,但是一个有着比较高启动开销的索引类型可能希望把这个值设置为非零.

indexSelectivity (索引选择性)应该设置为在索引扫描过程中主表记录里将被检索出的部分.如果是一个松索引的场合,这个数字将明显地比实际传递给给出的资格条件的记录部分高.

开销计算

一次典型的开销计算器将象下面这样进行:

    计算和返回基于给出的资格条件的将要访问的主表的记录的数量.如果不知到任何索引类型相关的信息,则使用标准的优化器函数 clauselist_selectivity():
    *indexSelectivity = clauselist_selectivity(root, indexQuals,
                                               lfirsti(rel->relids));
    计算(估计)在扫描过程中将要被访问的索引记录数.对于许多索引类型,这个数字等于 indexSelectivity 乘以索引里面的记录数量,但是它可以更多.(请注意索引在页面里的大小和记录可以从结构 IndexOptInfo 里获得.)

    计算(估计)在扫描过程中将要被检索出的索引页面数.这个数字可以只是 indexSelectivity 乘以以页面数计算的索引的大小.

    计算索引访问开销.一个常间的计算器可以这样做:

        /*
         * 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 tuple.
         * All the costs are assumed to be paid incrementally during the scan
         * (我们一般性的假设是索引页面将被顺序读入,
         *   因此它们每个的开销为1.0,没有 random_page_cose.
         *   同样,我们计算每条索引记录的索引条件的开销.
         *   所有开销都假设是在扫描过程中逐步递增的.
         */
        *indexStartupCost = 0;
        *indexTotalCost = numIndexPages +
            (cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
开销计算器的例子可以在 src/backend/utils/adt/selfuncs.c 找到.

通常,一个  amcostestimate 函数的 pg_proc 记录会看起来象

prorettype = 0
pronargs = 7
proargtypes = 0 0 0 0 0 0 0
我们将零("opaque")用于所有参数,因为它们没有一个有在  pg_type 里面已知的类型.