web-dev-qa-db-ja.com

複数列のインデックスとパフォーマンス

複数列のインデックスを持つテーブルがあり、クエリで最大のパフォーマンスを得るためのインデックスの適切な並べ替えに疑問があります。

シナリオ:

  • PostgreSQL 8.4、約100万行のテーブル

  • 列の値c1には約100の異なる値を含めることができます。値が均等に分散されていると想定できるため、可能な値ごとに約10000行があります。

  • c21000の異なる値を持つことができます。すべての可能な値に対して1000行があります。

データを検索する場合、条件には常にこれら2つの列の値が含まれるため、テーブルにはc1とc2を組み合わせた複数列のインデックスがあります。フィルタリングに1つの列のみを使用するクエリがある場合、複数列インデックスで 列の順序付け の重要性について読みました。これは私たちのシナリオには当てはまりません。

私の質問はこれです:

フィルターの1つがはるかに小さなデータセットを選択するという事実を踏まえて、最初のインデックスが最も選択的なインデックス(より小さなセットを許可するもの)である場合、パフォーマンスを改善できますか?参照された記事のグラフィックを見るまで、私はこの質問を考えていませんでした。

enter image description here

マルチカラムインデックス に関する参照記事から取得した画像。

クエリは、フィルタリングに2つの列の値を使用します。フィルタリングに1つの列だけを使用するクエリはありません。それらのすべては:WHERE c1=@ParameterA AND c2=@ParameterB。このような条件もあります:WHERE c1 = "abc" AND c2 LIKE "ab%"

32
jap1968

回答

Webサイト_use-the-index-luke.com_を参照するので、次の章を検討してください。

インデックスを使用、Luke› Where句›範囲の検索›Greater、Less and BETWEEN

状況に完全に一致する例があります(2列のインデックス、1つはequalityでテストされ、もう1つはrangeでテストされます) 、(これらのニースインデックスグラフィックの詳細とともに) @ ypercubeのアドバイス が正確であり、それを合計する理由を説明します。

_Rule of thumb: index for equality first — then for ranges.
_

また、1つの列だけに適していますか?

1つの列に対するクエリに対して何をすべきかは明らかなようです。これらの関連質問の下での詳細とベンチマーク:

最初に選択性の低いカラム?

それとは別に、両方の列の等式条件のみがある場合はどうなりますか?

それは問題ではありません。実際に重要な独自の条件を受け取る可能性が高い列を最初に置きます。

このデモを検討するか、自分で再現してください。 10万行の2列の単純なテーブルを作成します。 1つは非常にfew、もう1つはlotsの異なる値を持つ:

_CREATE TEMP TABLE t AS
SELECT (random() * 10000)::int AS lots
     , (random() * 4)::int     AS few
FROM generate_series (1, 100000);

DELETE FROM t WHERE random() > 0.9;  -- create some dead tuples, more "real-life"

ANALYZE t;

SELECT count(distinct lots)   -- 9999
     , count(distinct few)    --    5
FROM   t;
_

クエリ:

_SELECT *
FROM   t
WHERE  lots = 2345
AND    few = 2;
_

_EXPLAIN ANALYZE_出力(キャッシュ効果を除外するには10が最適):

 tでのシーケンススキャン(コスト= 0.00..5840.84行= 2幅= 8)
(実際の時間= 5.646..15.535行= 2ループ= 1)
フィルター: ((lots = 2345)AND(few = 2))
バッファ:ローカルヒット= 443 
合計実行時間:15.557 ms 

インデックスを追加して再テスト:

CREATE INDEX t_lf_idx ON t(lots, few);
Tでのt_lf_idxを使用したインデックススキャン(コスト= 0.00..3.76行= 2幅= 8)
(実際の時間= 0.008..0.011行= 2ループ= 1)
インデックス条件:((ロット= 2345)AND(少数= 2))
バッファー:ローカルヒット= 4 
合計ランタイム: 0.027ミリ秒

他のインデックスを追加し、再テストします。

_DROP INDEX t_lf_idx;
CREATE INDEX t_fl_idx  ON t(few, lots);_
Tのt_fl_idxを使用したインデックススキャン(コスト= 0.00..3.74行= 2幅= 8)
(実際の時間= 0.007..0.011行= 2ループ= 1)
インデックス条件:((少数= 2)AND(ロット= 2345))
バッファ:ローカルヒット= 4 
総実行時間: 0.027ミリ秒
37

あなたが言うように、これらの2つの列を含むクエリが、両方の列の等価チェックである場合、たとえば、次のようになります。

_WHERE c1=@ParameterA AND c2=@ParameterB
_

これを気にしないでください。違いがあるとは思えませんし、もしあるとしてもそれはごくわずかです。もちろん、データとサーバー設定を使用して、いつでもテストできます。 DBMSのバージョンが異なると、最適化に関して動作が若干異なる場合があります。

インデックス内の順序は、1つの列のみのチェック、不等式条件、または1つの列の条件と別の列のグループ化など、他のタイプのクエリで重要になります。

2つの注文のいずれかを選択する場合、less選択列を最初に配置することを選択します。列yearおよびmonthを含むテーブルについて考えます。 _WHERE year = 2000_条件または_WHERE year BETWEEN 2000 AND 2013_またはWHERE (year, month) BETWEEN (1999, 6) AND (2000, 5)が必要になる可能性が高くなります。

タイプ_WHERE month = 7 GROUP BY year_のクエリは確かに必要かもしれませんが(7月に生まれた人を検索)、それほど頻繁ではありません。もちろん、テーブルに保存されている実際のデータによって異なります。今のところ、1つの注文を選択し、_(c1, c2)_と言うと、後で_(c2, c1)_を使用していつでも別のインデックスを追加できます。


pdate、 OPのコメントの後:

このような条件もあります:_WHERE c1 = 'abc' AND c2 LIKE 'ab%'_

このタイプのクエリは、_c2_列の範囲条件が正確であり、_(c1, c2)_インデックスが必要な場合。リバースタイプのクエリもある場合:

_WHERE c2 = 'abc' AND c1 LIKE 'ab%'
_

次に、_(c2, c1)_インデックスもあるとよいでしょう。

11
ypercubeᵀᴹ