153
  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の複合インデックスは使用していません。私はすべきですか?

4

9 に答える 9

124

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

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 つのディメンションに対してクエリを実行している場合は、複合インデックスが最適です。どちらが最初であるかは問題ではありません (ほとんどの場合)。

于 2009-12-01T03:31:43.747 に答える
64

次の 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 番目のインデックスは複合インデックスです。ご覧のように、geolng は geolat によってインデックス付けされているため、geolng に基づいて検索できますが、geolat または「geolat AND geolng」で検索することもできます (geolng は第 2 レベルのインデックスであるため)。

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

于 2009-12-01T03:09:21.700 に答える
21

複合インデックスの機能について誤解がある可能性があります。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値はほとんど繰り返されません。との複合インデックスは次のようになりますgeolatgeolng

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 次元の問題を解決するのではなく、R ツリーと 2 次分割を使用して検索を高速化するだけである可能性があります。

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

于 2011-12-24T03:54:28.750 に答える
8

複合インデックスは次の場合に役立ちます

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

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

最も近いものを探す-- 問題が本当に最適化に関するものである場合

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

その場合、両方のディメンションを実際に処理できるインデックスはありません。

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

PARTITIONs緯度範囲のストライプです。はPRIMARY KEY、有効な行が同じブロックにある可能性が高いように、意図的に経度から開始します。order by... limit...ストアド ルーチンは、十分な数のコーヒー ショップ (または何でも) ができるまで、ターゲットの周りに「正方形」を実行および拡大するための厄介なコードを調整します。また、大圏の計算と日付変更線と極の処理も処理します。

もっと

別のブログを書きました。lat/lng 検索を行う 5 つの方法を比較 ます。そして、それらが特定のケースに最適であることを指摘しています:

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

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

于 2015-11-28T01:14:36.257 に答える
6

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

  • 構造の完全性を強化する
  • 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 は結果を取得するために一時ファイルまたはファイルソートを使用する必要はありません。複合キーがなければ、これは不可能であり、非常に非効率的なクエリになります。

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

于 2012-12-29T15:22:29.057 に答える
1

黒と白はありません、1つのサイズがすべての答えに適合します。

クエリの作業負荷が1つのメリットを享受する場合は、複合(または複数列)インデックスを使用する必要があります。

これを判断するには、クエリの作業負荷をプロファイリングする必要があります。

複合インデックスは、クエリがそのインデックスから完全に満たされる場合に機能します。つまり、クエリに必要なすべての列がインデックスに含まれている(カバーされている)場合です。

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

于 2009-12-01T02:52:39.300 に答える
1

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

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

于 2013-12-30T11:19:55.480 に答える
1

空間検索を行うには、地理的領域を非常に迅速に検索できるR-Treeアルゴリズムが必要です。まさにこの仕事に必要なもの。

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

于 2009-12-01T04:46:40.147 に答える
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) クエリに実際にかかる時間を測定するのは簡単です。

于 2009-12-01T03:13:57.183 に答える