Chapter 19. インデックスコスト概算関数

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

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

すべてのインデックスアクセスメソッドは、プランナ/オプティマイザから 使用できるようにコスト概算関数を提供しなければなりません。この関数の プロシージャ OID はアクセスメソッドのpg_am の フィールドamcostestimateで与えられます。

Note: Postgres 7.0 以前では、インデックスを指定したコスト概算関数の 登録には、違った方法が使われていました。

関数 amcostestimate は、インデックスと共に使えることが決定された WHERE 句のリストを与えられます。この関数はインデックスにアクセス するコストの概算と WHERE 句の選択度(これはインデックススキャンの間に 抽出されるメインテーブルのタプルの一部分です)を返さなくてはいけません。 単純な場合だと、ほとんどすべてのコスト概算の作業がオプティマイザの 標準ルーチンを呼び出すことでできます。amcostestimate 関数を持つことの 意味は、標準概算を改善することが可能な場合に、インデックスアクセスメソッド がインデックス型を指定した 知識を供給することを許可することです。

それぞれの amcostestimate 関数はシグニチャをもたなければいけません:

void
amcostestimate (Query *root,
                RelOptInfo *rel,
                IndexOptInfo *index,
                List *indexQuals,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity);
   
最初の4つのパラメータは引数です:

root

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

rel

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

index

インデックスそのもの。

indexQuals

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

最後の三つのパラメータは参照引渡しの返り値です。

*indexStartupCost

インデックス開始プロセスのコストに設定されています。

*indexTotalCost

インデックスプロセスの全体のコストに設定されています。

*indexSelectivity

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

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

インデックスアクセスコストは src/backend/optimizer/path/costsize.c で使われるユニットで計算されるべきです。順番に並んだディスクブロック 取り出しは 1.0 のコストがかかっており、順不同の取り出しは random_page_cost のコストがかかっています。そして、一つのインデックスタプルの処理のコスト は通常 cpu_index_tuple_cost とされるべきです(これはユーザが調節可能な オプティマイザパラメータです)。更に、cpu_operator_cost が相応の 数に増えると、インデックス処理の間に呼び出される比較演算子も コストに加算されます(特にindexQuals 自身の評価では)。

アクセスコストは、インデックス自身のスキャンと関係する全てのディスクと CPU コストも含むべきですが、インデックスで指定されるメインテーブルの タプルの処理や抽出は含みません。

"開始コスト" は、最初のタプルの取り出しを始める前に使い果たされなければ ならない総計スキャンコストの一部です。ほとんどのインデックスでは これはゼロとされますが、高い開始コストを持つインデックス型ではゼロ以外 に設定した方が良いかもしれません。

indexSelectivity は、インデックススキャンの間に抽出されるメインテーブルの タプルの概算された一部分として設定されるべきです。浪費の多いインデックス の場合はこの値が、与えられた制約条件を実際に通過するタプルの部分 よりも高くなることがよくあります。

コスト概算

典型的なコスト概算は次のように進められます:

  1. 与えられた制約条件に基づいて訪れられるメインテーブルのタプルの一部分を 概算して返します。インデックス型を指定した知識がない場合、標準 のオプティマイザ関数である clauselist_selectivity() を使って下さい。

    *indexSelectivity = clauselist_selectivity(root, indexQuals,
                                               lfirsti(rel->relids));
         

  2. スキャン中に訪れられるインデックスタプルの数を概算します。多くの インデックス型にとって、これは indexSelectivity とインデックスの中 にあるタプルの数を掛けたものと等しいですが、それより多い場合もあります。 (インデックスのページ数とタプルは IndexOptInfo 構文から得ることが できます。)

  3. スキャン中に抽出されるインデックスページの数を概算します。 これは indexSelectivity とインデックスのページ数ちょうどでしょう。

  4. インデックスアクセスコストを計算します。一般的な概算では このようなことをするでしょう:

    
    /* ここでの全体的な憶測は、インデックスページが順番に読まれ、それぞれに
         * random_page_cost ではなく 1.0 のコストがかかるということです。 
         * さらに、それぞれのインデックスタプルの indexquals の評価も加算され 
         * ます。すべてのコストはスキャンの間増加しながらかかっていくと
         * 仮定されます。
         */
        *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
   
pg_type で知られる型を持っている引数がないため、ここでは 0 (ゼロ)を 使っています。