MySQLのLIKE検索でインデックスが効かない理由

インデックスがあるのにLIKE検索が遅い理由

MySQLで検索機能を実装すると、多くの人が一度は混乱します。
「インデックスを貼ったのに遅い」

特に次のようなSQLです。

SELECT * FROM users
WHERE name LIKE '%田中%';

インデックスが設定されているのに、なぜかフルスキャンになります。
EXPLAINを見ると index ではなく ALL が表示されます。

ここでよく言われる説明はこうです。

「前方一致じゃないから」

これは正しいのですが、実は不十分です。
LIKE検索が効かない理由は、単に「%が先頭にあるから」だけではありません。

本当の理由は、MySQLのインデックスの仕組みにあります。

B-Treeインデックスは辞書と同じ

MySQLの通常のインデックスはB-Treeです。
これは本の索引と同じ構造です。

例えば電話帳を想像してください。

  • 「田中」で始まる人 → すぐ探せる
  • 「中」を含む人 → 最初から全部見る必要がある

つまりB-Treeは「先頭から並んでいる」ことを前提にしています。

LIKE '田中%'

この場合、先頭が分かっています。
MySQLは「田中」から始まる位置へジャンプできます。

しかし、

LIKE '%田中%'

では先頭が分かりません。
どこから読むべきか判断できないため、全行確認します。

これがフルテーブルスキャンです。

なぜ英語では問題になりにくいのか

英語圏のシステムでは、この問題はそれほど深刻になりません。
理由は検索方法です。

英語検索は次の形式が多いです。

  • email = 'user@example.com'
  • name LIKE 'john%'

つまり前方一致が中心です。
一方、日本語検索は違います。

  • 部分一致
  • 曖昧検索
  • キーワード検索

ユーザーは文字列の途中を検索します。
この時点でB-Treeインデックスと相性が悪くなります。

インデックスが使われないもう1つの理由

実は、先頭一致でもインデックスが使われない場合があります。

原因は照合順序(COLLATION)です。

WHERE name LIKE 'たなか%'

一見問題なさそうですが、MySQLは比較前に文字列正規化を行うことがあります。
大文字小文字、全角半角、アクセントなどを統一して比較します。

この処理が入ると、単純な範囲検索ができません。
結果としてインデックスが無効になります。

つまり、LIKE検索が効かない原因は2つあります。

  • 先頭が不明
  • 比較規則が複雑

EXPLAINで確認する

次のコマンドで実際の挙動が確認できます。

EXPLAIN SELECT * FROM users
WHERE name LIKE '田中%';

ここで type が range ならインデックス使用、ALL なら全件検索です。

開発中は速くても、本番で遅くなることがあります。
理由はデータ量です。
数百件では差が出ませんが、数百万件で顕著になります。

よくある誤解

よく行われる対処があります。

  • インデックスを増やす
  • 複合インデックスにする

しかし LIKE '%文字%' では基本的に効果はありません。
どれだけインデックスを増やしても、開始位置が分からないためです。

これは設計の問題で、最適化の問題ではありません。

現実的な解決策

対策はいくつか存在します。

前方一致に変える

UIをオートコンプリートにします。
入力途中で候補表示にするだけで、検索速度は劇的に改善します。

FULLTEXTインデックス
ALTER TABLE users
ADD FULLTEXT INDEX ft_name(name);

部分一致検索が可能になります。
ただし日本語ではngramパーサが必要です。

検索専用カラムを作る

例えば「ひらがな変換カラム」を用意します。
検索キーを限定することで範囲検索が可能になります。

注意点とリスク

FULLTEXT検索には重要な制限があります。

  • 短い単語が検索できない
  • 完全一致ではない
  • スコア順になる

従来のLIKE検索と同じ結果にはなりません。

また、検索ロジックを変えると、ユーザー体験も変わります。
単に高速化だけの問題ではありません。

LIKE検索の本質

LIKE検索は「遅い機能」ではありません。
用途が限定された機能です。

B-Treeインデックスは「キー検索」に最適化されています。
文章検索には向いていません。

つまり問題はSQLの書き方ではなく、
「データベースに任せてよい検索か」という設計です。

LIKE検索が遅いとき、最適化より先に
検索の役割を整理する方が、結果として安定したシステムになります。