web-dev-qa-db-ja.com

複合インデックスの順序

次のようなクエリがあるとします。

SELECT *
FROM table_a
    JOIN table_b USING (id)
WHERE table_b.column = 1

idにインデックスとcolumnにインデックスがありますが、多くの場合、このようなクエリの効率を向上させることができる両方の複合インデックスを追加します。私の質問は、インデックス内の列の順序に関するものです。試行錯誤の結果、DBMSは結合されたインデックスを最初に優先することも、WHEREインデックスを最初に優先することもあります。

上記のクエリには、どのキーの順序が最適に機能するかを知るために遵守できる厳格な規則がありますか?

通常は、両方のインデックスを追加し、クエリでEXPLAINを実行して、どちらが望ましいかを確認してから、もう一方を削除します。しかし、このプロセスは、インデックスの順序の決定に関連するロジックをよりよく理解することで改善できるように感じます。

5
billynoah

目安としては、複合インデックスの先頭列をできるだけ選択的にすることです。これを想像するための良い方法は、電話帳のアナロジーを使用することです。電話帳で誰かを見つける必要があり、2つのインデックスがあるとします。1つ目は姓、名です。 2番目はFirstName、LastNameです。 John Xylophoneという名前の人物を見つけるには、どのインデックスを使用しますか? Xylophoneエントリがほとんどないため、LastName、Firstnameインデックスを使用します。姓がXylophoneのJohnエントリをすべて探すよりも、はるかに短い時間で済みます。

したがって、idの選択性が高く、columnの選択性が低い場合は、インデックスを(id, column)にする必要がありますが、columnの選択性が高い場合は、そしてidは選択性が低いため、おそらく(column, id)として定義されたインデックスを使用することでメリットが得られます。

idで必要な行数が大幅に減少したときに、xの2つのテーブルを(column, id)で結合すると、where column = xのインデックスが使用される場合があります。参加しました。

4
Max Vernon

for this query

_SELECT *
FROM table_a
    JOIN table_b USING (id)
WHERE table_b.column = 1
_

最適な方法はそれを実行することです

  1. WHERE句はフィルタリングを提供するので、それを利用しましょう。つまり、_table_b_ で始まるcolumnにインデックスを作成します。 (後で合成するかどうかについて説明します。)したがって、オプティマイザーはそのインデックスを使用して_table_b_の行を検索します。
  2. それらの各行について、JOINから_table_a_まで。 (_LEFT JOIN_ではなくJOINが使用されていることに注意してください。_LEFT JOIN_は別の話です。)
  3. _table_a_にアクセスするには、idで始まるインデックスが必要です。 (注:USING(id)は_table_a.id = table_b.id_を意味します。)

これまでのところ、

_b:  INDEX(column)
a:  INDEX(id)   -- though it probably exists as PRIMARY KEY(id)
_

カバー?

2つのテーブルに他にどのような列があるかわかりません。列が非常に少ない場合は、「カバーする」インデックスを作成したくなるかもしれません。これは、SELECTall必要な列anywhereを含むインデックスです。利点は、インデックスのBTreeのみを参照し、データBTreeを操作する必要がないため、パフォーマンスがいくらか高速になることです。

_table_b_の場合、INDEX(column, id)と言いたくなるでしょう。これらの2つの列のみが存在する場合、それは(そして「カバーする」)良いでしょう。しかし、おそらくもっと列があります。したがって、おそらくINDEX(column)を実行するだけの価値があります。

_table_a_の場合、私はidが_PRIMARY KEY_(定義上、一意であり、インデックス)であると想定しています。したがって、それ以上は必要ありません。

ボトムライン:上記の2つの単一列インデックスを使用します。

そして、この例は「複合」インデックスについて何も例示していません。詳細については、

カーディナリティと範囲
カーディナリティとコンポジット
単一列のインデックス
インデックス作成クックブック

しかし、多くの場合、このようなクエリの効率を向上させることができる両方の複合インデックスを追加します...

より良い例

私が言ったように、あなたの例は質問を例示していません。それで、「いつ複合インデックスを使用する必要があるのか​​」と答えようとしますか?多くの場合があります(リンクを参照)。簡単なケースをあげましょう。

_WHERE x = 1
  AND y > 2
_

関連する特性は次のとおりです。

  • xyは同じテーブルにあります。 (2つのテーブルにまたがってインデックスを作成することはできません。)
  • ANDが使用されます。 (ORは最適化できません。)
  • テストの1つは_=_を使用することです。 (両方が範囲である場合、複合は役に立ちません。)
  • yは「範囲」です(例:_y>2_、_y LIKE 'm%'_、_y BETWEEN ... AND ..._)。

一般的なルールは

  1. すべての_=_列を最初に配置します(この例ではx
  2. 最後にone範囲列を配置(y

つまり、INDEX(x,y)を注文する必要があります。

_WHERE x = 1 AND y = 2_(両方の_=_)の場合、notINDEX(x,y)またはINDEX(y,x)のどちらを使用しているかに関係ありません。

もう1つのヒント:_ENGINE=InnoDB_を使用すると、_PRIMARY KEY_列が各セカンダリキーに暗黙的に追加されます。したがって、INDEX(column)INDEX(column, id)と同じです。しかし、この事実はこの議論では役割を果たしていません。

私はここ(および他の場所)で他の回答に同意していないことに気付いていますが、私は自分の立場に立っています。

4
Rick James

上記のクエリには、どのキーの順序が最適に機能するかを知るために遵守できる厳格な規則がありますか?

あなたが与えた例では、結合順序を自由に変更できる場合、最善の策は複合インデックスを持たないことです:

create table table_a(id integer, dummy_a integer);
create index index_a on table_a(id);
create table table_b(id integer, col integer, dummy_b integer);
create index index_b on table_b(col);
explain select * from table_b join table_a using(id) where table_b.col=1;
 id | select_type |テーブル|パーティション|タイプ|可能性のあるキー|キー| key_len | ref |行|フィルター済み|追加
-:| :------- :------ | :--------- | :--- | :------------ | :------ | :------ | :------------------------------------- | ---:| -------:| :---------- 
 1 |シンプル| table_b | null| ref | index_b | index_b | 5 | const | 1 | 100.00 | where 
の使用1 |シンプル| table_a | null| ref | index_a | index_a | 5 | fiddle_YRFDITQONPXNRMDBQSYV.table_b.id | 1 | 100.00 | null

db <> fiddle ---(ここ