MySQL のインデックスが使われない原因と対処法
インデックスを作ったのに使われない。そんな経験はありませんか?インデックスが期待どおりに機能しない原因と、その対処法を解説します。
原因1: カラムに関数や演算を適用している
WHERE 句でカラムに関数を適用すると、インデックスは使われません。
WHERE YEAR(created_at) = 2024
WHERE created_at >= ‘2024-01-01’ AND created_at < ‘2025-01-01’
MySQL 8.0 では関数インデックスを作成できますが、通常はクエリを書き換えるほうが簡単です。
-- 関数インデックスの例(MySQL 8.0以降)
ALTER TABLE logs ADD INDEX idx_year ((YEAR(created_at)));原因2: 暗黙の型変換が発生している
カラムの型と比較値の型が異なると、暗黙の型変換が発生してインデックスが効かなくなります。
-- phone_number が VARCHAR 型の場合
SELECT * FROM users WHERE phone_number = 09012345678; -- 数値として比較→インデックス無効
SELECT * FROM users WHERE phone_number = '09012345678'; -- 文字列として比較→インデックス有効文字列カラムには文字列リテラルで、数値カラムには数値リテラルで比較しましょう。
原因3: LIKE で前方一致以外を使っている
LIKE 演算子は、前方一致の場合のみインデックスが使われます。
WHERE name LIKE ‘tanaka%’
WHERE name LIKE ‘%tanaka’ や WHERE name LIKE ‘%tanaka%’
部分一致や後方一致が必要な場合は、FULLTEXT インデックスや外部の全文検索エンジンを検討してください。
原因4: OR 条件を使っている
OR で複数条件を結合すると、インデックスが使われないことがあります。
-- インデックスが効きにくい
SELECT * FROM products WHERE category_id = 1 OR price < 1000;異なるカラムの OR 条件は、UNION に書き換えると改善することがあります。
SELECT * FROM products WHERE category_id = 1
UNION
SELECT * FROM products WHERE price < 1000;原因5: NULL との比較
IS NULL や IS NOT NULL は、MySQL のバージョンや設定によってインデックスの使用可否が変わります。
-- インデックスが使われないことがある
SELECT * FROM users WHERE deleted_at IS NULL;NULL を多用するカラムでは、デフォルト値を設定して NULL を避ける設計も検討してください。
原因6: カーディナリティが低い
カーディナリティ(値の種類の数)が低いカラムのインデックスは、効果が薄いと判断されて使われないことがあります。
-- status が 'active' / 'inactive' の2種類しかない場合
SELECT * FROM users WHERE status = 'active'; -- インデックスより全件スキャンが速いと判断される可能性オプティマイザは統計情報を基に判断します。テーブルの大部分が該当する条件では、インデックスを使わないほうが速いこともあるのです。
原因7: 複合インデックスの列順が合っていない
複合インデックスは、左端のカラムから順に使われます。
-- インデックス: (category_id, brand_id)
SELECT * FROM products WHERE brand_id = 5; -- category_id がないのでインデックス無効
SELECT * FROM products WHERE category_id = 1; -- 有効
SELECT * FROM products WHERE category_id = 1 AND brand_id = 5; -- 有効クエリで使う条件に合わせて、インデックスの列順を設計する必要があります。
インデックスが使われているか確認する方法
EXPLAIN の key 列を見れば、どのインデックスが使われたかわかります。
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';key が NULL なら、インデックスは使われていません。possible_keys に候補があるのに key が NULL の場合は、オプティマイザが使わないと判断したということです。
強制的にインデックスを使わせる
どうしてもインデックスを使わせたい場合は、ヒント句を使えます。
SELECT * FROM users FORCE INDEX (idx_email) WHERE email = 'test@example.com';ただし、オプティマイザの判断を上書きするため、状況が変わると逆効果になることもあります。使用は慎重に判断してください。
まとめ
EXPLAIN を使って、インデックスが使われているか常に確認する習慣をつけましょう。