著者: 2000 年 1 月 24 日に Tom Lane(<tgl@sss.pgh.pa.us>)によって書かれました。
注意: これは最終的には、新しいインデックスアクセスメソッドの書き方についてもっと大きい章の一部になるかもしれません。
全てのインデックスアクセスメソッドは、プランナ/オプティマイザが使用できるようにコスト概算関数を提供しなければいけません。 この関数のプロシージャ 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つのパラメータは入力です。
処理されている問い合わせ。
指定されたインデックスがあるリレーション。
インデックスそのもの。
インデックス制約句のリスト(暗黙的に論理積されます)。 NIL リストは使用可能な制約がないことを表します。
最後の 4 つのパラメータは参照渡しの出力です。
インデックスの起動処理にかかるコストに設定されます。
インデックス処理の全体のコストに設定されます。
インデックスの選択度に設定されます。
インデックススキャンの順番と背後のテーブルの順番間の相関係数に設定されます。
コスト概算関数は、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までの間の値を取る)相関として設定されるべきです。 この値は、メインテーブルから行を取り出すためのコスト概算を調整するために使用されます。
コスト概算
典型的なコスト概算は次のように進められます。
与えられた制約条件に基づいて訪れられるメインテーブルの行の割合を概算して返します。 インデックス型固有のの知識体系を持たない場合、標準のオプティマイザの関数である clauselist_selectivity() を使用してください。
*indexSelectivity = clauselist_selectivity(root, indexQuals, rel->relid, JOIN_INNER);
スキャン中に訪れられるインデックスの行数を概算します。 多くのインデックス型では、これは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 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;
インデックスの相関を概算します。一つのフィールドに対する単純な順番のインデックスでは、これは pg_statistic から入手することができます。 相関が未知の場合、概算を用心深く考えると 0(無相関)となります。
コスト概算関数の例は src/backend/utils/adt/selfuncs.c にあります。
通常は、amcostestimate 関数の pg_proc 項目では、8 つの引数をすべて内部型として宣言する必要があります (これらの関数の型は SQL で使用可能な型ではないからです)。 そして、戻り値の型は void 型として宣言する必要があります。