第 49章インデックスコスト推定関数

著者: 2000 年 1 月 24 日に Tom Lane() によって書かれました。

注意: これは最終的には、新しいインデックスアクセスメソッドの書き方に ついてもっと大きい章の一部にたまたまなるかもしれなりません。

すべてのインデックスアクセスメソッドは、プランナ/オプティマイザが 使用できるようにコスト概算関数を提供しなければいけません。この関数 のプロシージャ 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);
   

最初の4つのパラメータは入力です。

root

処理されている問い合わせ。

rel

指定されたインデックスがあるリレーション。

index

インデックスそのもの。

indexQuals

インデックス制約句のリスト(暗黙的に論理積されます)。 NIL リストは使える制約がないことを表します。

最後の 4 つのパラメータは参照渡しの出力です。

*indexStartupCost

インデックスの起動処理にかかるコストに設定されます。

*indexTotalCost

インデックス処理の全体のコストに設定されます。

*indexSelectivity

インデックスの選択度に設定されます。

*indexCorrelation

インデックススキャンの順番と背後のテーブルの順番間の相関係数に 設定されます。

コスト概算関数は、SQL やその他の手続言語ではなく、C 言語で書かれなけれ ばいけないことに注意してください。理由はプランナ/オプティマイザの内部 データ構造にアクセスしなければいけないためです。

インデックスアクセスコストは src/backend/optimizer/path/costsize.c で使われる、 順番に並んだディスクブロックの取り出しには 1.0 のコストが、順不同の取り出し には random_page_cost のコストが、そして、1つのインデックスタプルの処理には 通常 (これはユーザが調節可能なオプティマイザのパラメータである) cpu_index_tuple_cost というコストがかかるといった、単位で計算されなけらば なりません。更に、インデックス処理、(特に indexQuals 自身の評価)の間に 呼び出される比較演算全てに対して、cpu_operator_cost に適当な係数をかけた コストがかかります。

アクセスコストは、インデックス自身のスキャンと関係するすべてのディスクと CPU コストも含むべきですが、インデックスで識別されるメインテーブルの行の 処理や抽出にかかるコストは含めてはいけません。

"開始コスト"は、最初の行の取り出しを始める前に費やされる、 スキャンコスト全体の一部です。ほとんどのインデックスではこれは 0 と されますが、高い開始コストを持つインデックス型では 0 以外に設定した方が よいかもしれません。

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 から入手することができます。 相関が未知の場合、概算を用心深く考えると 0(無相関)となります。

コスト概算関数の例は src/backend/utils/adt/selfuncs.c にあります。

通常は、amcostestimate 関数の pg_proc 項目では、8 つの引数をすべて内部型として宣言する必要があります (これらの関数の型は SQL で使用可能な型ではないからです)。そして、戻り値の 型は void 型として宣言する必要があります。