★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

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

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

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

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

Note: 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,
                                            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;
         

  5. インデックスの相関を概算します。1つのフィールドに対する単純な順番のインデックスでは、これは pg_statistic から入手することができます。相関が未知の場合、控えめな概算は 0 (無相関)となります。

コスト概算関数の例は、src/backend/utils/adt/selfuncs.c で見つけることができます。

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