「なぜかサイトが重い」と感じたら、MySQLのインデックス不足が原因かもしれません。EXPLAINコマンドとスロークエリログを使った実践的な調査・改善手順を解説します。
こんな悩み、ありませんか?
「サーバースペックは十分なはずなのに、なぜかページの表示が遅い」「お問い合わせフォームや検索機能を使うと、なぜかクルクルが止まらない」——そんな症状に悩んでいるWeb担当者の方は少なくありません。
FivenineDesignでは、中小企業のサイトリニューアルや保守案件に長年携わってきましたが、表示速度の問題を調査すると、「MySQLのインデックスが正しく機能していない」 というケースに驚くほど頻繁に遭遇します。WordPressサイトもLaravelで構築したシステムも、データが積み重なるにつれて同じ問題が顔を出してきます。
この記事では、「インデックスって聞いたことはあるけど、本当に効いているかどうか確認したことはない」という中級者の方に向けて、実際の現場で使っている調査手順と改善方法を、コード例を交えながら解説します。
なぜインデックスが「効いていない」状態になるのか
インデックスとは、データベースの検索を高速化するための索引です。本の巻末索引と同じ仕組みで、「この列でよく検索されるなら、あらかじめ整理しておく」という仕掛けです。
しかし、インデックスを貼っただけで安心してしまうのがよくある落とし穴です。次のような状況では、せっかくのインデックスがまったく機能しません。
- WHERE句の列に関数を適用している(例:
WHERE DATE(created_at) = '2024-01-01') - LIKE句をワイルドカードから始めている(例:
WHERE title LIKE '%お知らせ%') - 複合インデックスの順序が合っていない(インデックスを
(A, B)で作ったのにWHERE B = ?だけで検索している) - テーブルの行数が少なすぎてMySQL自身がフルスキャンを選択している
- データが増えたことで、以前は速かったクエリが遅くなった
あるクライアント(神奈川県内の製造業、WordPressで会員向けポータルを運営)の案件では、会員登録数が5,000件を超えたあたりからマイページの表示が急激に遅くなりました。調査してみると、user_idでのJOINに必要なインデックスが一切貼られていないテーブルが2つ見つかり、毎回フルスキャンを行っていました。インデックスを追加した後は、問題のクエリの実行時間が2.3秒から0.04秒に短縮されました。体感でも「別物」と感じるほどの変化でした。
実践:遅いクエリを見つけて改善する手順
ステップ1:スロークエリログを有効にする
まず、MySQLが「遅いと判断したクエリ」を自動で記録する機能を使います。本番環境での設定変更は慎重に行ってください。my.cnf(またはmy.ini)に以下を追記します。
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow_query.log
long_query_time = 1
log_queries_not_using_indexes = 1
long_query_time = 1 は「1秒以上かかったクエリを記録する」という意味です。最初は 0.5 など低めの値で試してみてもいいでしょう。設定後はMySQLを再起動するか、動的に変更します。
-- 再起動なしで一時的に有効にする場合
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';
ステップ2:mysqldumpslow でログを集計する
スロークエリログが溜まったら、mysqldumpslowコマンドで整理します。
# 実行時間の合計が多い順にトップ10を表示
mysqldumpslow -s t -t 10 /var/log/mysql/slow_query.log
出力結果に「Count」(何回実行されたか)と「Time」(平均実行時間)が表示されます。回数が多くて遅いクエリが最優先の改善対象です。
ステップ3:EXPLAINでクエリを解剖する
問題のクエリが特定できたら、EXPLAINコマンドで内部動作を確認します。
EXPLAIN SELECT * FROM wp_posts
INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_posts.post_status = 'publish'
AND wp_postmeta.meta_key = 'member_type'
AND wp_postmeta.meta_value = 'premium';
出力結果の中で特に注目すべき列は以下の3つです。
| 列名 | 確認ポイント |
|---|---|
type |
ALL(フルスキャン)が出ていたら要注意。refやrangeが理想 |
key |
NULLならインデックスが使われていない |
rows |
検索のために読んだ推定行数。多いほどコストが高い |
type: ALL かつ key: NULL の組み合わせは、インデックスが機能していない確実なサインです。
ステップ4:インデックスを追加して改善する
原因が特定できたら、適切なインデックスを追加します。
-- post_statusだけで検索する場合
ALTER TABLE wp_posts ADD INDEX idx_post_status (post_status);
-- 追加後に効果を確認
EXPLAIN SELECT * FROM wp_posts WHERE post_status = 'publish';
SHOW INDEX FROM テーブル名; で現在のインデックス一覧を事前に確認しておきましょう。重複したインデックスは逆にINSERT/UPDATEのパフォーマンスを下げます。
よくある失敗パターンと対処法
現場でよく見かける「やりがちなミス」をまとめました。インデックスを追加したのに速くならない場合は、まずここを疑ってください。
**NG:** `WHERE DATE(created_at) = '2024-06-01'`
**OK:** `WHERE created_at >= '2024-06-01 00:00:00' AND created_at < '2024-06-02 00:00:00'`
開発・運用でお困りなら
システム開発
設計から運用まで、堅牢なシステムを構築します
※ 通常1営業日以内にご返信します
まとめと次のステップ
インデックスの問題は「サイトが重くなってきた」という症状が出てから気づくことがほとんどです。しかし調査方法さえわかれば、原因の特定と改善は決して難しくありません。スロークエリログとEXPLAINを組み合わせるだけで、問題のクエリを数時間以内に特定できるケースが大半です。
ただし、実際の本番環境での作業はリスクを伴います。「ログの確認はできたけど、ALTER TABLEを本番で実行するのは怖い」「そもそもどのインデックスが最適か自信がない」という場合は、無理に進めるより専門家に相談する方が安全です。誤ったインデックス設計はむしろパフォーマンスを悪化させることもあります。
Fivenine Designでは、既存サイトのパフォーマンス診断から改善実装まで対応しています。「まずクエリの状況だけ見てほしい」という段階からでも、お気軽にご相談ください。