日本語検索が遅いのはSQLではなくMySQL設定
アプリケーションの検索機能を作ると、ある段階で必ず言われます。
「検索が遅いです」
そして開発者はこう考えます。
- インデックスが足りない?
- SQLが重い?
- サーバスペックが低い?
もちろんそれらが原因のこともあります。
しかし、日本語検索の場合、実際には違うことが多いです。
MySQLの設定です。
特に次の条件が揃うと、SQLをどれだけ改善しても速くなりません。
- 日本語の文字列検索
- LIKE検索
- 文字列カラムにインデックスあり
「インデックスがあるのに遅い」という現象は、MySQLの動作仕様に関係しています。
日本語検索でインデックスが効かない理由
まず結論から言うと、次のSQLは基本的に高速化できません。
SELECT * FROM articles WHERE title LIKE '%検索%';
これはMySQLが怠けているわけではありません。
仕組み上、インデックスが使えない条件だからです。
インデックスは「先頭一致」でしか効果を発揮しません。
- LIKE '検索%' → インデックス使用
- LIKE '%検索%' → インデックス不使用
ここまでは有名です。
しかし日本語では、もう1つの問題が発生します。
文字数ではなく「バイト長」で比較している
MySQLのB-Treeインデックスは、文字列を「文字」ではなくバイト列として扱います。
英語なら1文字1バイトに近い挙動になります。
しかし日本語は違います。
- 1文字 = 3〜4バイト
つまり比較コストが増えます。
さらに、照合順序(COLLATION)が関係します。
MySQLは単純なバイト比較ではなく「言語規則に基づいた比較」を行います。
その結果、1行ごとに文字列評価が発生します。
テーブルスキャンに近い状態になります。
COLLATIONが遅さを引き起こす
特に影響が大きいのが次の設定です。
- utf8mb4_unicode_ci
- utf8mb4_0900_ai_ci
これらは正確な言語比較を行います。
つまり、「似ている文字」を同一扱いします。
例として次があります。
- は と ば
- カタカナとひらがな
- アクセント付き文字
人間には便利ですが、データベースにとっては重い処理です。
検索のたびに正規化と比較を行います。
英語データ中心のシステムと比べ、日本語検索が遅くなる主な理由がここにあります。
よくある誤った対処
検索が遅いと、よく次の対応が行われます。
- サーバのCPUを上げる
- メモリを増やす
- インデックスを増やす
しかし、LIKE '%文字%' の検索では効果が限定的です。
ボトルネックはCPUではなく「比較処理」だからです。
効率的な解決策
解決方法はいくつかあります。
前方一致検索に変える
WHERE title LIKE '検索%'
これだけでインデックスが使われます。
検索UIを「候補表示型」にするだけで大きく改善します。
ngram FULLTEXTを使う
MySQLにはFULLTEXT検索があります。
特に日本語ではngramパーサが有効です。
ALTER TABLE articles ADD FULLTEXT INDEX ft_title(title) WITH PARSER ngram;
これにより部分一致検索が実用的な速度になります。
COLLATIONを見直す
用途によっては utf8mb4_bin を使うことで比較コストを下げられます。
ただし検索結果の仕様が変わるため、安易な変更は危険です。
注意点とリスク
FULLTEXTには重要な注意点があります。
- 最小文字数制限
- ストップワード
- 検索結果の順序
特に日本語では「の」「する」などが除外される場合があります。
期待した結果が出ないことがあります。
また、既存データが多い場合、インデックス作成に長時間ロックが発生します。
なぜ日本語だけ問題になるのか
MySQLは元々英語圏で発展しました。
そのため「単語区切り」が前提です。
英語:
word boundary が明確
日本語:
区切りがない
つまり、日本語検索は「検索」ではなく
文字列解析に近い処理になります。
データベースが苦手な領域です。
結局どう考えるべきか
日本語検索の遅さはSQLの問題でも、サーバ性能の問題でもないことがあります。
RDBMSの役割の限界に近い問題です。
MySQLは「構造化データの検索」には非常に強いですが、
「文章検索」は得意分野ではありません。
そのため、要件によっては検索エンジン(Elasticsearchなど)を検討する方が合理的な場合もあります。
検索が遅いとき、SQLを疑う前に
「DBに向いている処理か」を見直す方が、結果的に早く解決することが多いです。