こういった状況は、MySQLを自前運用しているサーバー管理者なら一度は経験するはずです。MySQLにはスロークエリログという標準の診断機能があり、設定を有効にしておくだけで閾値を超えた遅いSQLを自動的にファイルへ記録してくれます。
この記事では、スロークエリログを有効化するための設定方法から、
mysqldumpslowコマンドによるログの集計・分析、EXPLAINを使った実行計画の読み方とインデックス改善の基本まで、Linuxサーバー上でのMySQL自前運用(セルフホスト)を前提に解説します。RDS等のマネージドDBは扱いません。動作確認環境: Rocky Linux 9.5(Kernel 5.14.0-503.el9.x86_64)/ MySQL 8.4.5 LTS
この記事のポイント
・スロークエリログはmy.cnfにslow_query_log=ONを追記するだけで有効化できる
・long_query_timeを1秒に設定すると実用的な閾値になる(デフォルト10秒は見逃しが多い)
・mysqldumpslow -s t -t 10でボトルネックSQLを合計実行時間の多い順に絞り込める
・EXPLAINのtype=ALLは全件スキャンを意味し、インデックス追加で劇的に改善できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
スロークエリログとは何か・なぜ必要か
MySQLには、指定した実行時間(long_query_time)を超えたSQLを自動的にログファイルへ書き出す機能があります。これがスロークエリログ(slow query log)です。データベースのパフォーマンス問題を「勘」で追いかけるのは非常に非効率です。「どのSQLが遅いか」「1日に何回実行されているか」「合計の実行時間はどれくらいか」といった情報が揃って初めて、対策の優先順位がつけられます。スロークエリログはこれらを自動的に収集してくれる、実務では欠かせない仕組みです。
スロークエリログがなければ、パフォーマンス問題の調査は以下のような非効率な手順になりがちです。
・アプリのソースコードを読んで「怪しいSQL」を推測する
・開発環境でSQLを手動で実行して実行時間を計測する
・本番環境のトラフィックを再現できないため原因を特定できない
スロークエリログを有効にしておくことで、本番環境で実際に発行されている遅いSQLをそのまま記録できます。「現場の証拠」として最も信頼性の高い情報源です。
スロークエリログを有効化する(設定ファイル編集とON/OFF確認)
1. 設定ファイルへの追記(my.cnf / mysqld.cnf)
MySQLの設定ファイルは環境によって場所が異なります。・Rocky Linux 9(RHEL系):
/etc/my.cnf または /etc/my.cnf.d/mysql-server.cnf・Ubuntu 24.04 LTS:
/etc/mysql/mysql.conf.d/mysqld.cnf[mysqld]セクションに以下の設定を追記します。[mysqld] # スロークエリログの有効化 slow_query_log = ON # ログの出力先 slow_query_log_file = /var/log/mysql/slow-query.log # スロークエリと判定する閾値(秒) long_query_time = 1 # インデックスを使わないクエリも記録する log_queries_not_using_indexes = ON
# Rocky Linux 9 / systemd環境でのMySQL再起動 sudo systemctl restart mysqld
# ディレクトリ作成と権限設定 sudo mkdir -p /var/log/mysql sudo chown mysql:mysql /var/log/mysql
2. MySQLを再起動せずにオンラインで有効化する
本番サービス中で再起動できない場合は、SET GLOBAL文を使ってオンラインで有効化できます。この方法は再起動後に元の設定に戻るため、恒久的な設定には必ず設定ファイルへの追記も行ってください。# MySQL CLIでrootまたは管理者ユーザーとして接続後に実行 mysql> SET GLOBAL slow_query_log = 'ON'; mysql> SET GLOBAL slow_query_log_file = '/var/log/mysql/slow-query.log'; mysql> SET GLOBAL long_query_time = 1; mysql> SET GLOBAL log_queries_not_using_indexes = 'ON';
3. 有効化されているかをSHOW VARIABLESで確認する
設定が正しく反映されているかを確認します。mysql> SHOW VARIABLES LIKE 'slow_query%'; +---------------------+--------------------------------+ | Variable_name | Value | +---------------------+--------------------------------+ | slow_query_log | ON | | slow_query_log_file | /var/log/mysql/slow-query.log | +---------------------+--------------------------------+ mysql> SHOW VARIABLES LIKE 'long_query_time'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | long_query_time | 1.000000 | +-----------------+----------+
slow_query_logがONになっていれば、スロークエリログは有効です。long_query_timeとその他の主要パラメータを理解する
スロークエリログの動作を制御する主要なパラメータを整理します。| パラメータ | デフォルト値 | 推奨設定 | 説明 |
|---|---|---|---|
slow_query_log |
OFF | ON | スロークエリログの有効/無効 |
long_query_time |
10秒 | 1秒 | スロークエリと判定する実行時間の閾値 |
log_queries_not_using_indexes |
OFF | ON(初期調査時) | インデックス未使用クエリをすべて記録 |
min_examined_row_limit |
0(制限なし) | 1000など | この行数以上を検索したクエリのみ記録 |
log_slow_admin_statements |
OFF | ON(ALTER追跡時) | ALTER TABLE等の管理系SQLも記録 |
デフォルトの10秒という閾値は実務ではほぼ機能しません。ユーザーが10秒以上かかるSQLを常用しているサービスは、すでに深刻な問題を抱えているはずです。調査の出発点としては1秒に設定し、ログ量が多すぎる場合に2秒・3秒へ段階的に引き上げるのが現場での定石です。
log_queries_not_using_indexesの注意点:
このオプションを有効にすると、小さなテーブルへのフルスキャンなど「遅くはないがインデックス未使用」のクエリも記録されます。初期調査が終わったら
min_examined_row_limitを組み合わせるか、このオプションをOFFに戻すとログ量を抑えられます。mysqldumpslowでログを集計・分析する
スロークエリログが蓄積されたら、mysqldumpslowコマンドで集計します。このコマンドはMySQL本体に同梱されているため、追加インストールは不要です。1. 基本的な使い方(合計実行時間でソート)
# 合計実行時間が長い順に上位10件を表示 mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log
Reading mysql slow query log from /var/log/mysql/slow-query.log Count: 147 Time=3.82s (561s) Lock=0.00s (0s) Rows=1234.5 (181472), app[app]@webserver01.example.com SELECT * FROM orders WHERE status = 'S' AND created_at > 'S' Count: 89 Time=2.14s (190s) Lock=0.01s (1s) Rows=8921.3 (794197), app[app]@webserver01.example.com SELECT u.*, p.* FROM users u LEFT JOIN profiles p ON u.id = p.user_id WHERE u.last_login > 'S' Count: 312 Time=0.98s (305s) Lock=0.00s (0s) Rows=45.2 (14102), app[app]@webserver01.example.com SELECT COUNT(*) FROM access_logs WHERE session_id = 'S' AND created_at BETWEEN 'S' AND 'S'
・Count: 同じパターンのSQLが実行された回数
・Time=3.82s (561s): 平均実行時間 / 合計実行時間
・Lock=0.00s (0s): 平均ロック待ち時間 / 合計ロック待ち時間
・Rows=1234.5 (181472): 平均返却行数 / 合計返却行数
mysqldumpslowは同じパターンのSQLをまとめて集計します(値の部分をSやNに抽象化します)。Count×Timeの合計が大きいものが、サーバー全体への影響が最も大きいSQLです。2. ソートオプションの使い分け
| オプション | ソートキー | 用途 |
|---|---|---|
mysqldumpslow -s t |
合計実行時間 | サーバー全体への影響が最大のSQLを探す |
mysqldumpslow -s at |
平均実行時間 | 1回あたりの遅さが最悪のSQLを探す |
mysqldumpslow -s c |
実行回数 | 最も頻繁に発行されているSQLを探す |
mysqldumpslow -s l |
合計ロック待ち時間 | ロック競合が疑われる場合 |
mysqldumpslow -s r |
合計返却行数 | 大量データを返すSQLを探す |
3. ログを直接確認する(grepで絞り込む)
詳細な実行情報(HOSTや実際のパラメータ値)が必要な場合は、ログファイルを直接確認します。# Query_timeが3秒以上のエントリを前後5行ずつ表示する grep -A 5 "Query_time: [3-9]" /var/log/mysql/slow-query.log | head -60
# Time: 2026-08-17T09:23:41.512847Z # User@Host: app[app] @ webserver01.example.com [192.168.1.101] # Query_time: 4.213847 Lock_time: 0.000312 Rows_sent: 2847 Rows_examined: 987654 SET timestamp=1755427421; SELECT * FROM orders WHERE status = 'processing' AND created_at > '2026-08-01 00:00:00';
EXPLAINで実行計画を読み解き、ボトルネックを特定する
mysqldumpslowで「遅いSQL」が特定できたら、次はEXPLAINでそのSQLの実行計画を確認します。実行計画とは「MySQLがそのSQLをどのように実行するか」の内部戦略です。1. EXPLAINの基本的な使い方
-- EXPLAINはSELECT文の前に付けるだけ(\Gで縦表示) EXPLAIN SELECT * FROM orders WHERE status = 'processing' AND created_at > '2026-08-01 00:00:00'\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: orders partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 987654 filtered: 1.50 Extra: Using where
2. typeカラムで全件スキャン(ALL)を発見する
EXPLAINの出力でまず確認すべきはtypeカラムです。このカラムはテーブルへのアクセス方式を示し、パフォーマンスへの影響度を判断する最重要指標です。| type値 | アクセス方式 | 評価 |
|---|---|---|
const |
PRIMARY KEYまたはUNIQUEインデックスで1行確定 | 最速 |
eq_ref |
JOINでPRIMARY/UNIQUEを使って1行取得 | 高速 |
ref |
非UNIQUEインデックスを使用 | 良好 |
range |
インデックスを使った範囲スキャン | 許容範囲 |
index |
インデックス全体をスキャン | 要確認 |
ALL |
テーブル全件スキャン(フルスキャン) | 問題あり |
type: ALLは、MySQLがテーブルの全行を読み込んで条件に合う行を探していることを意味します。テーブルが小さければ問題になりませんが、数十万行以上のテーブルでALLが発生すると深刻なパフォーマンス劣化の原因になります。3. Extraカラムの「Using filesort」「Using temporary」に注目する
Extraカラムには追加の実行情報が入ります。以下の2つが現れた場合は要注意です。・Using filesort: ORDER BY句の並び替えをインデックスではなく別プロセス(ファイルソート)で行っている。大量データでは極めて重くなる
・Using temporary: GROUP BYやDISTINCTの処理に一時テーブルを使っている。メモリを圧迫し、ディスクに溢れると急激に遅くなる
これらが表示されるSQLは、適切なインデックスを設計することで解消できることが多いです。
MySQLのパフォーマンス問題はスロークエリログとEXPLAINで「見える化」できます。さらにLinuxサーバー全般の実務スキルを体系的に身につけたい方へ、20年以上の現場経験を持つエンジニアが指導します。
スロークエリを解消するインデックスとクエリ改善の基本
1. インデックスを追加してALLをrangeに変える
先ほどのordersテーブルの例で、type: ALLを解消します。WHERE句で使われているstatusとcreated_atに複合インデックスを追加します。-- 複合インデックスの追加 CREATE INDEX idx_orders_status_created ON orders (status, created_at);
EXPLAIN SELECT * FROM orders WHERE status = 'processing' AND created_at > '2026-08-01 00:00:00'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: orders type: range possible_keys: idx_orders_status_created key: idx_orders_status_created key_len: 8 ref: NULL rows: 312 filtered: 100.00 Extra: Using index condition
typeがALLからrangeに改善し、スキャン行数が987,654行から312行に劇的に減少しました。2. 複合インデックスの基本的な考え方
複合インデックス(複数カラムを組み合わせたインデックス)を設計する際の基本原則を押さえておきます。・等価条件のカラムを先頭に置く:
WHERE status = '...' AND created_at > '...'の場合、等価比較(=)のstatusを先頭、範囲比較(>)のcreated_atを後ろに配置する・カーディナリティの高いカラムを先頭に置く: 値の種類が多い(絞り込みが強い)カラムを優先する
・ORDER BYのカラムをインデックスに含める: ソートに使うカラムをインデックスに含めるとUsing filesortを解消できることがある
3. インデックスが効かないパターン
インデックスを追加してもEXPLAINのtypeが改善しない場合があります。代表的なパターンを確認してください。・関数でラップされたカラム:
WHERE YEAR(created_at) = 2026はインデックスが使われない。WHERE created_at BETWEEN '2026-01-01' AND '2026-12-31'に書き直す・前方一致以外のLIKE:
WHERE name LIKE '%山田%'はインデックス不使用。全文検索が必要な場合はFULLTEXTインデックスを検討する・データ型の不一致: カラムがINT型なのに
WHERE id = '123'と文字列で比較するとインデックスが使われないことがある・OR条件:
WHERE status = 'A' OR category = 'B'はインデックスの効きが悪くなりやすい。UNIONへの書き換えを検討するログのローテーションと運用管理
スロークエリログを有効にしたままにすると、ディスクを圧迫します。定期的なローテーションが必要です。1. logrotateでスロークエリログを自動ローテーションする
MySQLをパッケージインストールした環境では、/etc/logrotate.d/mysqlにデフォルトのlogrotate設定が含まれていることが多いです。スロークエリログが含まれていない場合は以下の設定を追加します。# /etc/logrotate.d/mysql-slow の内容例 /var/log/mysql/slow-query.log { daily missingok rotate 14 compress delaycompress notifempty create 640 mysql mysql postrotate if test -x /usr/bin/mysqladmin && /usr/bin/mysqladmin ping &>/dev/null then /usr/bin/mysqladmin flush-slow-logs fi endscript }
flush-slow-logsコマンドはMySQLに新しいログファイルへの書き換えを指示します。これを忘れると、ローテーション後もMySQLは旧ファイルのinodeに書き続けます(ファイル名は変わっても内容は古いファイルに追記される状態になります)。2. ログサイズと空き容量の定期確認
# スロークエリログのサイズと最終更新日時を確認する ls -lh /var/log/mysql/slow-query.log # ディスク全体の空き容量も確認する df -h /var/log
よくあるトラブルと対処法
「スロークエリログが出力されない」場合の確認手順
設定したのにログが出力されない場合は、以下の順で確認してください。ステップ1: SHOW VARIABLESで設定を確認する
mysql> SHOW VARIABLES LIKE '%slow%'; +---------------------------+--------------------------------+ | Variable_name | Value | +---------------------------+--------------------------------+ | log_slow_admin_statements | OFF | | log_slow_extra | OFF | | slow_query_log | ON | | slow_query_log_file | /var/log/mysql/slow-query.log | +---------------------------+--------------------------------+
# ログファイルの所有者・権限を確認する ls -la /var/log/mysql/ # MySQLプロセスの実行ユーザーを確認する(通常はmysqlユーザー) ps aux | grep mysqld | grep -v grep
mysql)でない場合は、chown mysql:mysql /var/log/mysql/slow-query.logで修正します。MySQLが起動時にポート3306をリッスンしているか確認したい場合は、ssコマンドやlsofコマンドによるポート確認の方法も参考にしてください。ステップ3: エラーログを確認する
# MySQLエラーログの末尾を確認する(Rocky Linux 9の場合) sudo tail -50 /var/log/mysqld.log
「log_queries_not_using_indexesで大量ログが出る」場合の対処
このオプションは診断目的で有効にしますが、本番環境ではログが大量に出力されることがあります。min_examined_row_limitと組み合わせることでノイズを減らせます。-- 1000行以上スキャンしたクエリのみ記録するように制限する mysql> SET GLOBAL min_examined_row_limit = 1000;
log_queries_not_using_indexesをOFFに戻すことも忘れずに。mysql> SET GLOBAL log_queries_not_using_indexes = 'OFF';
「mysqldumpslowが実行できない」場合
mysqldumpslowはMySQLサーバーパッケージに含まれていますが、環境によってはパスが通っていないことがあります。# mysqldumpslowの場所を確認する which mysqldumpslow # 見つからない場合はfindで探す find /usr -name 'mysqldumpslow' 2>/dev/null # Rocky Linux 9でパスが通っていない場合はフルパスで実行 /usr/bin/mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log
本記事のまとめ
MySQLのスロークエリログを活用したパフォーマンス診断の全体手順をまとめます。| やりたいこと | コマンド / 設定 |
|---|---|
| スロークエリログを有効化する | slow_query_log = ON(my.cnfに追記) |
| 閾値を1秒に設定する | long_query_time = 1(my.cnfに追記) |
| オンラインで設定を変更する | SET GLOBAL slow_query_log = 'ON'; |
| 現在の設定を確認する | SHOW VARIABLES LIKE 'slow_query%'; |
| ログを合計実行時間順に集計する | mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log |
| SQLの実行計画を確認する | EXPLAIN SELECT ... \G |
| 全件スキャンを確認する | EXPLAINのtype列がALLでないかチェック |
| インデックスを追加する | CREATE INDEX idx_name ON table (column); |
| ログを自動ローテーションする | /etc/logrotate.d/にlogrotate設定を追加 |
mysqldumpslowで確認する習慣をつけておくと、パフォーマンス劣化の兆候を早期に捉えられるようになります。EXPLAINとインデックス設計はセットで理解しておくことで、問題の発見から解消まで一貫して対処できます。MySQLのパフォーマンスチューニングを理解したら、次はLinuxサーバー全体の運用スキルを体系的に固めませんか?
スロークエリ解析・インデックス設計・バックアップ設計など、現場で必要なDB運用の知識も含め、現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Linuxサーバーに自前でMySQLを導入する手順|公式リポジトリの登録から初期セキュア設定・自動起動まで
- この記事の属するカテゴリ:MySQLへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます