MySQLのインデックス
そもそもインデックスとは
リレーショナルデータベースにおいて、検索を速くするための仕組みです
挿入、削除、更新はインデックスがあるとむしろ遅くなります
PrestoとかHiveとかは同じSQLを使っていますが、インデックスはありません
インデックスの仕組み
MySQLのインデックスは、ほぼB-Treeに格納されます
B-tree
インデックスの仕組み
それぞれ格納されるデータ
- ブランチノード
- 検索キー
- リーフノードへのポインタ
- リーフノード
- 検索キー
- テーブルの行ID(Row ID)
RDBにおける
インデックス
ルートノード
ブランチノード
リーフノード
インデックスの仕組み
例えば以下のようなテーブルの場合
archer rider saber
- archer
- id=2への
ポインタ
- archer
- id=4への
ポインタ
idx_role
ルートノード
ブランチノード
(ソートされている)
リーフノード
create table servant (
id int primary key auto_increment,
name varchar(255),
role varchar(255),
index idx_role (role)
);
insert into servant(id, name, role)
values (1, ‘Arthur’, ‘saber’),
(2, ‘Emiya’, ‘archer’),
(3, ‘Iscandar’, ‘rider’),
(4, ‘Gilgamesh’, ‘archer’)
・・・・・・
インデックスの仕組み
正確にはこんな感じ
1ノードに複数のインデックスデータが
入っている
archer rider saber
- archer
- id=2への
ポインタ
- archer
- id=xへの
ポインタ
ルートノード
ブランチノード
(ソートされている)
リーフノード
・・・・・・
- archer
- id=4への
ポインタ
- rider
- id=xへの
ポインタ
インデックスの仕組み
正確にはこんな感じ
1ノードに複数のインデックスデータが
入っている
archer rider saber
- archer
- id=2への
ポインタ
- archer
- id=xへの
ポインタ
ルートノード
ブランチノード
(ソートされている)
リーフノード
・・・・・・
- archer
- id=4への
ポインタ
- rider
- id=xへの
ポインタ
rangeで取るときもソート
されているので速い
複合インデックス
- archer
- 1
- 行への
ポインタ
- archer
- 3
- 行への
ポインタ
ルートノード
ブランチノード
(ソートされている)
リーフノード
・・・・・・
- archer
- 1
- 行への
ポインタ
- archer
- 4
- 行への
ポインタ
create table servant (
id int primary key auto_increment,
name varchar(255),
role varchar(255),
master_user_id int,
index idx_1 (role, master_user_id)
);
archer
1
archer
3
一つの列にそれぞれのキーが入っている
重要なのはindexの設定順にソートされるという
こと
saber
1
複合インデックス
- archer
- 1
- 行への
ポインタ
- archer
- 3
- 行への
ポインタ
ルートノード
ブランチノード
(ソートされている)
リーフノード
・・・・・・
- archer
- 1
- 行への
ポインタ
- archer
- 4
- 行への
ポインタ
create table servant (
id int primary key auto_increment,
name varchar(255),
role varchar(255),
master_user_id int,
index idx_1 (role, master_user_id)
);
archer
1
archer
3
一つの列にそれぞれのキーが入っている
重要なのはindexの設定順にソートされるという
こと
role, master_user_idの順でソートされ
ている
where句がmaster_user_idだけの場合、
インデックスの使用は非効率
saber
1
カバリングインデックス
通常インデックスを用いた検索では
- インデックスにより対象のレコードを見つけ
- 実際のレコードを取得
という流れ。
しかし、複合インデックスを使うと
- インデックスにより対象のレコードを見つける
とインデックス内に取得に必要なレコードがあるため、実際のレコードを引かず
にすむ
カバリングインデックスの例
先程のテーブルの例だと以下のようなクエリで、カバリングインデックスとなる
create table servant (
id int primary key auto_increment,
name varchar(255),
role varchar(255),
master_user_id int,
index idx_1 (role, master_user_id)
);
select
role, master_user_id
from servant
where role in (‘archer’, ‘lancer’)
limit 100;
カーディナリティ
ユーザテーブルのカラム
- 性別
- 年齢
インデックスを張るならどっち?
カーディナリティ
ユーザテーブルのカラム
- 性別
- 年齢
インデックスを張るならどっち?
カーディナリティ
インデックスを張るときはカーディナリティが高いほうを選ぶ
「カーディナリティが高い」=「種類が多い」
B-Treeの観点からもなるべく結果を絞れるように作るほうがいいと分かるはず
実行計画
実際にインデックスを張ったあと、クエリを投げた際にインデックスが使われて
いるかをチェックする必要がある
explain select
role, master_user_id
from servant
where role in (‘archer’, ‘lancer’)
limit 100;
explainでよく出てくるもの
- typeカラム
- const: 一致したレコードが1件の場合
- eq_ref: joinをした場合に1件ヒットしている場合。primary key or unique
- ref: joinをした場合にインデックスを利用している場合
- index: インデックスツリーをフルスキャン
- all: フルスキャン
- Extraカラム
- using index: インデックスのみでテーブルアクセスしない(カバリングインデックス)
- using temporary: ソートなどをするために途中結果をメモリに載せます
詳しくはこちら: https://dev.mysql.com/doc/refman/5.6/ja/explain-output.html
パーティショニング
クエリ速度をあげるためにパーティショニングというのもある
物理的にファイルを分けるというやつ
インデックスとデータはセットで分割されるので、見る範囲が狭いほど速くなる
まとめ
MySQLはWebアプリケーションの基本なので抑えておきましょう
KVSや分散DBなどもありますが、RDSを知っておくと違いの分かる大人になれ
ます
参考文献
- https://dev.mysql.com/doc/refman/5.6/ja/introduction.html
- https://use-the-index-luke.com/ja

MySQLのインデックス入門