いつ複合インデックスを使用する必要がありますか?


133
  1. いつデータベースで複合インデックスを使用する必要がありますか?
  2. 複合インデックスを使用することによるパフォーマンスへの影響は何ですか?)
  3. なぜ複合インデックスを使用する必要があるのですか?

たとえば、私はhomesテーブルを持っています:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

それは私は両方のために複合インデックスを使用するために理にかなっていますgeolatし、geolngそのようなこと:

私は交換します:

  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),

と:

KEY `geolat_geolng` (`geolat`, `geolng`)

その場合:

  • どうして?
  • 複合インデックスを使用することによるパフォーマンスの影響は何ですか?)

更新:

多くの人が私が実行するクエリに完全に依存していると述べているので、以下は実行される最も一般的なクエリです:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

更新2:

次のデータベーススキーマの場合:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `primary_photo_group_id` int(10) unsigned NOT NULL default '0',
  `customer_id` bigint(20) unsigned NOT NULL,
  `account_type_id` int(11) NOT NULL,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `num_of_beds` tinyint(3) unsigned NOT NULL,
  `num_of_baths` decimal(3,1) unsigned NOT NULL,
  `num_of_floors` tinyint(3) unsigned NOT NULL,
  `description` text collate utf8_unicode_ci,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  `display_status` tinyint(1) NOT NULL,
  `date_listed` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `contact_email` varchar(100) collate utf8_unicode_ci NOT NULL,
  `contact_phone_number` varchar(15) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`home_id`),
  KEY `customer_id` (`customer_id`),
  KEY `city` (`city`),
  KEY `num_of_beds` (`num_of_beds`),
  KEY `num_of_baths` (`num_of_baths`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
  KEY `account_type_id` (`account_type_id`),
  KEY `display_status` (`display_status`),
  KEY `sqft` (`sqft`),
  KEY `price` (`price`),
  KEY `primary_photo_group_id` (`primary_photo_group_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;

次のSQLを使用します。

EXPLAIN SELECT  homes.home_id,
                    address,
                    city,
                    state,
                    zip,
                    price,
                    sqft,
                    year_built,
                    account_type_id,
                    num_of_beds,
                    num_of_baths,
                    geolat,
                    geolng,
                    photo_id,
                    photo_url_dir
            FROM homes
            LEFT OUTER JOIN home_photos ON homes.home_id = home_photos.home_id
                AND homes.primary_photo_group_id = home_photos.home_photo_group_id
                AND home_photos.home_photo_type_id = 2
            WHERE homes.display_status = true
            AND homes.geolat BETWEEN -100 AND 100
            AND homes.geolng BETWEEN -100 AND 100

EXPLAINは次を返します:

id  select_type  table        type  possible_keys                                    key                  key_len  ref     rows  Extra
----------------------------------------------------------------------------------------------------------
1   SIMPLE       homes        ref   geolat,geolng,display_status                     display_status       1        const   2     Using where
1  SIMPLE        home_photos  ref   home_id,home_photo_type_id,home_photo_group_id   home_photo_group_id  4        homes.primary_photo_group_id   4  

EXPLAINコマンドの読み方がよくわかりません。これは見栄えが悪いか。現在、geolatとgeolngに複合インデックスを使用していません。私はすべきですか?

回答:


111

メリットのあるクエリを使用する場合は、複合インデックスを使用する必要があります。次のような複合インデックス:

index( column_A, column_B, column_C )

これらのフィールドを使用して、結合、フィルタリング、場合によっては選択を行うクエリにメリットがあります。また、そのコンポジット内の列の左端のサブセットを使用するクエリにもメリットがあります。したがって、上記のインデックスは必要なクエリも満たします

index( column_A, column_B, column_C )
index( column_A, column_B )
index( column_A )

しかし、それは(少なくとも直接ではなく、より良いインデックスがない場合は部分的に役立つ可能性があります)必要なクエリには役立ちません

index( column_A, column_C )

column_Bが欠落していることに注目してください。

元の例では、2つのディメンションの複合インデックスは、両方のディメンションまたは左端のディメンションを単独でクエリするクエリに主に役立ちますが、右端のディメンションを単独ではクエリしません。常に2つのディメンションをクエリしている場合は、複合インデックスが適しています。どちらが最初であるかは関係ありません(おそらく)。


1
マーク、元の投稿を更新しました(更新2)。これは私の実際のクエリです。私の実際のdbスキーマ。そして、EXPLAINコマンドが返すもの。したがって、この情報を使用して-複合インデックスを使用する必要があります。私はまだはっきりしていません。前もって感謝します。
テディ

マーク、回答の複合インデックスはindex(column_C)を満たしていますか?
ボリスD.テハロフ2013

私はあなたの質問を理解しているのかわかりません。ただし、index(A、B、C)が列Cでフィルタリングするクエリに役立つかどうかを尋ねている場合、答えは通常「いいえ」となり、インデックスはフィルタリングに使用されません。ただし、ABCのサブセットのみを選択している場合は、インデックスを使用してテーブルスキャンを削除できます。したがって、それは異なりますが、関連しています。しかし、フィルターを有効にするためのインデックスの一般的な使用については、答えはノーです。
Mark Canlas 2013

1
-1は、複合インデックスはに役立ちませWHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???。最初のフィールドの後で停止します。「Question Overflow」からの回答がその理由を説明しています。
Rick James、

1
@felwithe MySQLは、クエリ内の各テーブルごとに1つのインデックスのみを使用できます(例外が存在します。たとえば、インデックスのマージ)。理想的には、クエリ内のテーブルは、すべてのwhere句、テーブル結合、group-by、order-byに単一のインデックスを使用する必要があることを意味します。そのため、各列の個別のインデックスが常に機能するとは限りませんが、複合インデックスが効果を発揮します。
AKHIL MATHEW

56

次の3つのクエリがあるとします。

クエリI:

SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4

クエリII:

SELECT * FROM homes WHERE `geolat`=42.9

クエリIII:

SELECT * FROM homes WHERE `geolng`=36.4

列ごとに個別のインデックスがある場合、3つのクエリはすべてインデックスを使用します。MySQLでは、複合インデックス(geolatgeolng)がある場合、クエリIとクエリII(複合インデックスの最初の部分を使用)のみがインデックスを使用します。この場合、クエリIIIでは全表検索が必要です。

上の複数列索引マニュアルのセクション、明らかに私が手動再入力する必要はありませんので、複数の列のインデックスは、どのように動作するかを説明します。

MySQLのリファレンスマニュアルのページ

複数列のインデックスは、インデックス付きの列の値を連結して作成された値を含む、ソートされた配列と見なすことができます

geolat列とgeolng列に分離インデックスを使用する場合、テーブルに2つの異なるインデックスがあり、独立して検索できます。

INDEX geolat
-----------
VALUE RRN
36.4  1
36.4  8
36.6  2
37.8  3
37.8  12
41.4  4

INDEX geolng
-----------
VALUE RRN
26.1  1
26.1  8
29.6  2
29.6  3
30.1  12
34.7  4

複合インデックスを使用する場合、両方の列に1つのインデックスしかありません。

INDEX (geolat, geolng)
-----------
VALUE      RRN
36.4,26.1  1
36.4,26.1  8
36.6,29.6  2
37.8,29.6  3
37.8,30.1  12
41.4,34.7  4

RRNは相対レコード番号です(簡単にするために、IDと言うことができます)。最初の2つのインデックスは別々に生成され、3番目のインデックスは複合です。ご覧のように、geolatによってインデックスが作成されているため、geongに基づいて検索できますが、geolatまたは「geolat AND geolng」で検索することもできます(geolngは第2レベルのインデックスであるため)。

また、MySQLがインデックスを使用する方法のマニュアルセクションもご覧ください


1
実際、これらのクエリはありません。私のクエリは元の投稿に記載されています。私の質問は、正方形のグリッド内の家を返すことです。私は空間について知っており、距離を計算しようとはしていません。特定の地理グリッド(たとえば、近隣/都市/郡)内のすべての家を表示しようとしているときに、複合インデックスを使用することが意味があるかどうかを知りたいだけです
Teddy

Eyazici、元の投稿を更新しました(更新2)。これは私の実際のクエリです。私の実際のdbスキーマ。そして、EXPLAINコマンドが返すもの。したがって、この情報を使用して-複合インデックスを使用する必要があります。私はまだはっきりしていません。事前の感謝
テディ

@「実際には、これらのクエリはありません。」実際には、基本的なロジックを説明するために簡単なWHERE条件を使用しています。列で条件付き(つまりWHERE)を使用する場合、MySQLは可能な限りインデックスを使用しようとします。「x BETWEEN a AND b」は「x> a AND x <b」に似ています。クエリ条件でgeolng列とgeolat列の両方を使用しました。複合インデックス「(geolat、geolng)」を使用する場合、「AND geolng BETWEEN ??? AND ???」条件付きはインデックスの利点を得ません(これはMySQLの場合です)。したがって、シナリオでは列ごとに個別のインデックス使用する必要があります。
Emre Yazici、

わかりません。常に両方の列を含むクエリを実行するときにgeolatとgeolngに別々のインデックスを使用する必要があるのはなぜですか
Teddy

1
いいえ。「範囲」に遭遇すると(と同様BETWEEN)、インデックスのそれ以上のフィールドは考慮されません。したがって、複合インデックスはこれ以上優れていません。
リックジェームズ

19

複合インデックスの機能について誤解がある可能性があります。多くの人whereは、あなたの場合geolatとで、句がインデックス付きの列をカバーしている限り、複合クエリを使用して検索クエリを最適化できると考えていますgeolng。さらに詳しく見てみましょう。

私は家の座標に関するあなたのデータはそのようなランダムな小数になると信じています:

home_id  geolat  geolng
   1    20.1243  50.4521
   2    22.6456  51.1564
   3    13.5464  45.4562
   4    55.5642 166.5756
   5    24.2624  27.4564
   6    62.1564  24.2542
...

以来geolatgeolng価値観はほとんど繰り返されません。上の複合インデックスgeolatとは、geolng次のようになります。

index_id  geolat  geolng
   1     20.1243  50.4521
   2     20.1244  61.1564
   3     20.1251  55.4562
   4     20.1293  66.5756
   5     20.1302  57.4564
   6     20.1311  54.2542
...

したがって、複合インデックスの2番目の列は基本的に役に立たない!複合インデックスを使用したクエリの速度は、おそらくgeolat列のみのインデックスと同様になります。

Willが述べたように、MySQLは空間拡張サポートを提供します。空間点は、2つの別々のlat lng列ではなく、1つの列に格納されます。このような列には空間インデックスを適用できます。ただし、効率は私の個人的な経験に基づいて過大評価される可能性があります。空間インデックスは2次元の問題を解決せず、2 次分割のRツリーを使用して検索を高速化するだけの可能性があります。

トレードオフは、空間ポイントが座標を格納するために8バイトの倍精度数を使用したため、より多くのメモリ消費することです。私が間違っていたら訂正してください。


5

複合インデックスは次の点で非常に強力です。

  • 構造の整合性を強化する
  • FILTERED idでの並べ替えを有効にする

構造の完全性を強化する

複合インデックスは、別のタイプのインデックスではありません。整合性を主キーとして適用することで、テーブルに必要な構造を提供できます。

MysqlのInnodbはクラスタリングをサポートしており、次の例は複合インデックスが必要になる理由を示しています。

友人のテーブルを作成するには(つまり、ソーシャルネットワークの場合)、2つの列が必要ですuser_id, friend_id

テーブル構造

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

おかげで、主キー(PK)は一意であり、複合PKを作成することにより、Innodbはuser_id, friend_id新しいレコードが追加されたときに重複が存在しないことを自動的にチェックします。friend_id = 2たとえば、ユーザーが複数のレコード(関係リンク)を持つべきではないため、これは予想される動作です。

複合PKがなければ、代理キーを使用してこのスキーマを作成できます。

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

ここで、新しいレコードが追加されるたびに、その組み合わせを持つ以前のレコードがuser_id, friend_idまだ存在していないことを確認する必要があります。

そのため、複合インデックスは構造の整合性を強化できます。

フィルタリングされたIDでのソートを有効にする

一連のレコードを投稿の時間(タイムスタンプまたは日時)でソートすることは非常に一般的です。通常、これは特定のIDに投稿することを意味します。ここに例があります

テーブルUser_Wall_Posts(Facebookのウォールポストかどうかを考えます)

user_id (medium_int)
timestamp (timestamp)
author_id (medium_int)
comment_post (text)

Primary Key -> (user_id, timestamp, author_id)

すべての投稿に対してクエリを実行して検索しuser_id = 10、コメント投稿をtimestamp(日付)で並べ替えます。

SQLクエリ

SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES

複合PKにより、Mysqlはインデックスを使用して結果をフィルタリングおよびソートできます。Mysqlは、結果を取得するために一時ファイルまたはfilesortを使用する必要はありません。複合キーがないと、これは不可能であり、非常に非効率的なクエリが発生します。

そのため、複合キーは非常に強力であり、「検索したいcolumn_a, column_bので複合キーを使用します。」という単純な問題よりも適しています。現在のデータベーススキーマには、単一キーと同じ数の複合キーがあります。見落とさないでください複合キーの使用!


5

複合インデックスは、

  • 0個以上の "="句、および
  • 最大で1つの range句。

複合インデックスは2つの範囲を処理できません。これについては、索引クックブックで詳しく説明します。

最も近いものを見つける -質問が本当に最適化に関するものである場合

WHERE geolat BETWEEN ??? AND ???
  AND geolng BETWEEN ??? AND ???

その場合実際には両方の次元を処理できるインデックスはありません

代わりに、「箱から出して考える」必要があります。1つのディメンションがパーティショニングを介して実装され、もう1つのディメンションがを注意深く選択することによって実装される場合PRIMARY KEY、lat / lngルックアップの非常に大きなテーブルの効率が大幅に向上します。私のlatlngブログでは、地球上で「最も近い場所を見つける」方法を詳しく説明しています。コードが含まれています。

PARTITIONs緯度範囲のストライプです。PRIMARY KEY有用な行が同じブロック内にある可能性があるように意図経度始まります。ストアドルーチンorder by... limit...は、十分なコーヒーショップ(または何でも)ができるまで、ターゲットの周りに「四角」を成長させるための厄介なコードを調整します。また、大圏の計算と日付線と極の処理も行います。

もっと

私は別のブログを書きました。それは、緯度/経度の検索を行うための5つの方法を比較しています http://mysql.rjweb.org/doc.php/latlng#representation_choices (それは5の一つとして、上記で与えられたリンクを参照)、他の方法の一つがこれです、そして、それらは特定のケースに最適あると指摘しています:

INDEX(geolat, geolng),
INDEX(geolng, geolat)

つまり、2つのインデックスに両方の列があり、geolatとgeolngに単一列のインデックスがないことが重要です。


1

白黒はありません。1つのサイズですべての答えに対応します。

クエリの作業負荷が複合インデックスのメリットになる場合は、複合インデックスを使用する必要があります。

これを判別するには、クエリのワークロードをプロファイルする必要があります。

複合インデックスは、そのインデックスからクエリを完全に満たすことができるときに機能します。

更新(投稿された質問への編集に応じて):テーブルから*を選択している場合、複合インデックスが使用される場合がありますが、使用されない場合があります。確認するにはEXPLAIN PLANを実行する必要があります。


地理的位置データ(緯度と経度)に複合インデックスを使用することには意味がありますか?
テディ

1
それは、そのテーブルに対して行われているクエリに完全に依存します。
ミッチウィート

元の投稿を更新して、実行された最も一般的なクエリを含めました。上記を参照。
テディ

1

空間検索を行うには、地理的領域をすばやく検索できるRツリーアルゴリズムが必要です。まさにこの仕事に必要なもの。

一部のデータベースには空間インデックスが組み込まれています。Googleをすばやく検索すると、MySQL 5にそれらが含まれていることがわかります(SQLを見ると、MySQLを使用していると思います)。


1

複合インデックスは、group by句を最適化する場合に役立ちます(この記事http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.htmlを確認してください)。注目してください:

GROUP BYのインデックスを使用するための最も重要な前提条件は、すべてのGROUP BY列が同じインデックスの属性を参照し、インデックスがキーを順番に格納することです(たとえば、これはBTREEインデックスであり、HASHインデックスではありません)。


GROUP BY言及されなかった。
リック・ジェームズ

それはどこで言及されなかったのですか?:)それは私が参照した記事で明らかに言及されています。そして、それは尋ねられた質問に答えます:データベースで複合インデックスをいつ使うべきですか?複合インデックスを使用することによるパフォーマンスへの影響は何ですか?)なぜ複合インデックスを使用する必要があるのですか?
Alexander

訂正: GROUP BYOPは言及しませんでした。
リックジェームズ

確かに、それが答えでした。データベースで複合インデックスを使用するケースの1つです。
Alexander

0

私は@Mitchと一緒です、あなたのクエリに完全に依存しています。幸いにも、いつでもインデックスを作成および削除できます。また、クエリにEXPLAINキーワードを付加して、クエリアナライザーがインデックスを使用しているかどうかを確認できます。

あなたが見上げることになります場合は、正確な緯度/経度のペアをこのインデックスは、おそらく理にかなって。しかし、おそらく特定の場所から一定の距離内にある家を探すことになるので、クエリは次のようになります(ソースを参照)。

select *, sqrt(  pow(h2.geolat - h1.geolat,  2) 
               + pow(h2.geolng - h1.geolng, 2) ) as distance
from homes h1, homes h2
where h1.home_id = 12345 and h2.home_id != h1.home_id
order by distance

インデックスはまったく役に立たない可能性が非常に高いです。地理空間クエリの場合、あなたのような何か必要なこれを

更新:このクエリを使用:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

クエリアナライザーは、geolatのみのインデックス、geolngのみのインデックス、または両方のインデックスを使用できます。複合インデックスを使用するとは思わない。しかし、実際のデータセットでこれらの順列のそれぞれを試してから、(a)EXPLAINが何を伝えているかを確認し、(b)クエリが実際にかかった時間を測定するのは簡単です。


私は単に正方形のグリッド内で家に帰りたいと思っています。私は空間について知っているので、距離を計算しようとはしていません。私は単に正方形のグリッド内で家に戻りたいと思っており、それをすばやく実行したいと思っています。そのため、インデックスが正しく設定されていることを確認したいと思います。それは役に立ちますか?
テディ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.