たとえば、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に複合インデックスを使用していません。私はすべきですか?
有益なクエリを使用する場合は、複合インデックスを使用する必要があります。次のような複合インデックス:
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つのディメンションをクエリする場合は、複合インデックスを使用する方法がありますが、どちらが最初か(ほとんどの場合)は重要ではありません。
次の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では、複合インデックス(geolat
、geolng
)がある場合、クエリIおよびクエリII(複合インデックスの最初の部分を使用)のみがインデックスを使用します。この場合、クエリIIIでは全表検索が必要です。
Multiple-Column Indexes マニュアルのセクションでは、複数のカラムインデックスがどのように機能するかが明確に説明されているため、マニュアルを再入力したくありません。
複数列のインデックスは、インデックス付きの列の値を連結することで作成された値を含むソート済み配列と見なすことができます。
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でインデックス付けされているため、コンポジットのgeolngに基づいて検索できますが、geolatまたは「geolat AND geolng」(geolngは第2レベルのインデックスであるため)で検索できます。
また、 MySQLがインデックスを使用する方法 マニュアルセクションもご覧ください。
複合インデックスが何をするかについての誤解があるかもしれません。多くの人は、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
...
geolat
とgeolng
の値はほとんど繰り返されません。 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番目の列は基本的にuselessです!複合インデックスを使用したクエリの速度は、おそらくgeolat
列のインデックスとほぼ同じになるでしょう。
Willが述べたように、MySQLは spatial extension サポートを提供します。空間ポイントは、2つの個別のlat
lng
列ではなく、単一の列に格納されます。このような列には空間インデックスを適用できます。ただし、効率は私の個人的な経験に基づいて過大評価される可能性があります。空間インデックスは、2次元の問題を解決せず、R-Trees with quadratic splittingを使用して検索を高速化するだけである可能性があります。
トレードオフは、空間ポイント より多くのメモリを消費する であり、座標の格納に8バイトの倍精度数値を使用したためです。間違っている場合は修正してください。
複合インデックスは
複合インデックスはtwo範囲を処理できません。 index cookbook でこれについてさらに説明します。
最も近いものを探す-質問が本当にある場合最適化について
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
その後、noインデックスは実際に両方の次元を処理できます。
代わりに、「箱から出して考える」必要があります。 1つのディメンションがパーティション分割によって実装され、もう1つのディメンションがPRIMARY KEY
、lat/lngルックアップの非常に大きなテーブルの効率が大幅に向上します。私の latlng blog は、地球上で「最近傍を検索」を実装する方法の詳細に進みます。コードが含まれています。
PARTITIONs
は緯度範囲のストライプです。 PRIMARY KEY
は意図的に経度で始まるため、有用な行が同じブロックにある可能性があります。ストアドルーチンは、order by... limit...
そして、十分な数のコーヒーショップ(または何でも)ができるまでターゲットの周りに「正方形」を成長させます。また、大圏計算と日付変更線と極の処理も行います。
複合インデックスは次のような非常に強力です。
構造の完全性を強制
複合インデックスは、単なる別のタイプのインデックスではありません。整合性を主キーとして強制することにより、テーブルに必要な構造を提供できます。
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
などのレコード(関係リンク)を1つ以上持つべきではないため、予想される動作です。
複合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
を検索したいので、複合キーを使用します。現在のデータベーススキーマには、単一キーと同じ数の複合キーがあります。 。複合キーの使用を見落とさないでください!
複合インデックスは、group by
句(この記事を確認してください http://dev.mysql.com/doc/refman/5.0/en/group-by-optimization.html )。注目してください:
GROUP BYのインデックスを使用するための最も重要な前提条件は、すべてのGROUP BY列が同じインデックスの属性を参照し、インデックスがキーを順番に格納することです(たとえば、これはBTREEインデックスであり、HASHインデックスではありません)
空間検索を行うには、 R-Tree アルゴリズムが必要です。これにより、地理的領域を非常にすばやく検索できます。まさにこの仕事に必要なもの。
一部のデータベースには空間インデックスが組み込まれています。Googleの簡単な検索では、MySQL 5にそれらが含まれていることが示されています(SQLを見ると、MySQLを使用していると思われます)。
白黒はなく、1つのサイズですべての答えに適合します。
クエリの作業負荷が複合インデックスの恩恵を受ける場合は、複合インデックスを使用する必要があります。
これを判断するには、クエリの作業負荷をプロファイルする必要があります。
複合インデックスは、そのインデックスからクエリを完全に満たすことができるときに機能します。
更新(投稿された質問の編集への応答):テーブルから*を選択している場合、複合インデックスは使用される場合があります。 EXPLAIN PLAN を実行する必要があります。
私は@Mitchと一緒にいます、あなたのクエリに完全に依存しています。幸い、いつでもインデックスを作成および削除でき、クエリにEXPLAINキーワードを追加して、クエリアナライザーがインデックスを使用しているかどうかを確認できます。
exactlat/longペアを検索する場合、このインデックスはおそらく意味があります。ただし、特定の場所から一定の距離内にある家を探しているので、クエリは次のようになります( source を参照)。
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
インデックスはまったく役に立ちません。地理空間クエリの場合、 this のようなものが必要です。
更新:このクエリで:
SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???
クエリアナライザーは、geolat単独のインデックス、geolng単独のインデックス、または場合によっては両方のインデックスを使用できます。複合インデックスを使用するとは思わない。しかし、実際のデータセットでこれらの順列をそれぞれ試してから、(a)EXPLAINが何を示しているかを確認し、(b)クエリに実際にかかる時間を測定するのは簡単です。