MariaDB [(none)]> use development;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [development]> SELECT COUNT(*) FROM users;
+----------+
| count(*) |
+----------+
| 100000 |
+----------+
1 row in set (0.087 sec)
MariaDB [development]>
まずは実行計画から、確認します。
//typeがALL になっている
//type が ALL の場合、フルテーブルスキャンを意味します。つまり users テーブル全体を走査して条件に一致するレコードを探します。
MariaDB [development]> EXPLAIN SELECT * FROM users WHERE prefecture = 'Tokyo';
+------+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 98690 | Using where |
+------+-------------+-------+------+---------------+------+---------+------+-------+-------------+
1 row in set (0.012 sec)
MariaDB [development]>
試しに、index を貼って、実行計画を確認します。
MariaDB [development]> CREATE INDEX idx_prefecture ON users(prefecture);
Query OK, 0 rows affected (0.318 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [development]> SHOW INDEX FROM users;
+-------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Ignored |
+-------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
| users | 0 | PRIMARY | 1 | id | A | 98625 | NULL | NULL | | BTREE | | | NO |
| users | 0 | users_email_unique | 1 | email | A | 98625 | NULL | NULL | | BTREE | | | NO |
| users | 1 | idx_prefecture | 1 | prefecture | A | 6 | NULL | NULL | | BTREE | | | NO |
+-------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
3 rows in set (0.014 sec)
//typeがALLのまま。想定ではref になる想定でしてた
MariaDB [development]> EXPLAIN SELECT * FROM users WHERE prefecture = 'Tokyo';
+------+-------------+-------+------+----------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+------+----------------+------+---------+------+-------+-------------+
| 1 | SIMPLE | users | ALL | idx_prefecture | NULL | NULL | NULL | 98625 | Using where |
+------+-------------+-------+------+----------------+------+---------+------+-------+-------------+
1 row in set (0.010 sec)
MariaDB [development]>
インデックスを使わない判断をオプティマイザがしている 可能性が高い?
SHOW INDEX の結果を見ると、Cardinality は6になっています。これはオプティマイザが推定したユニーク値数です。今回投入した都道府県は7種類なので、概ね近い値になっています。
Tokyo
Kanagawa
Chiba
Saitama
Osaka
Aichi
Fukuoka
10万件のデータに対して、仮に均等分布なら、100000 / 7 = 14285件 が Tokyo になります。
インデックスを辿る → 1万4000件の行を探す → 実データを1万4000回 取りにいくよりも
全部読む → Tokyo だけを返すの方が早いと判断したのですかね。(オプティマイザがそう判断した可能性があります)これはレコード数を増やしたあとにも検証してみます。
MariaDB [development]> EXPLAIN SELECT * FROM users WHERE email = 'dmarquardt_tyQ6q_6a374fc15a2e4@example.com';
+------+-------------+-------+-------+--------------------+--------------------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+--------------------+--------------------+---------+-------+------+-------+
| 1 | SIMPLE | users | const | users_email_unique | users_email_unique | 1022 | const | 1 | |
+------+-------------+-------+-------+--------------------+--------------------+---------+-------+------+-------+
1 row in set (0.010 sec)
MariaDB [development]>
users_email_unique が効いて、type が const になっていますね。
ここでそれぞれのタイプを最後にまとめておきます。今回の検証では index を貼ると実行計画がどのように変わるのかを確認しました。今回の例では、10万レコード で 低カーディナリティ のカラム に対して、index を貼ってもまだ、ALL のままですが、、、次回はレコードを100万 にして検証をしてみたいと思います。
type
意味
速度感
const
PKやUNIQUEで1件特定
最速
eq_ref
JOIN先をPK/UNIQUEで1件特定
constの次
ref
インデックスを使った等価検索
高速
range
インデックスを使った範囲検索
やや高速
index
インデックス全走査
遅い
ALL
テーブル全走査
最も遅い
最後に
index を貼っても、必ず使われるわけでない、、、奥が深いですね。(検索結果が全体の数%〜10%程度に絞り込める場合に効果を発揮します。ヒット件数が多すぎる場合、インデックス経由で1件ずつランダムアクセスするよりも、マルチブロックリードでテーブル全体を一気にスキャン(ALL)した方が早いとオプティマイザが判断するため、インデックスが無視されます。)
コメント