MySQLのperformance_schemaで遅いクエリとロック待ちを特定する方法|events_statementsとwait_summaryを使った実務調査

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Linuxtips > データーベース管理 > MySQLのperformance_schemaで遅いクエリとロック待ちを特定する方法|events_statementsとwait_summaryを使った実務調査
「DBがなんとなく重い気がするけれど、どのクエリが原因か分からない。」
そういう状況でスロークエリログを有効にするのは正しいアプローチです。ただ、「今まさに詰まっているクエリ」や「ロック待ちで止まっているスレッド」は、過去の記録を見るスロークエリログでは掴みにくいことがあります。

この記事では、MySQL 8.0~8.4 LTS を対象に、MySQL に標準搭載されている performance_schema を使って、遅いクエリとロック待ちを特定する手順を解説します。RHEL 9系・Ubuntu 24.04 LTS を想定環境として説明します。コピー&ペーストで使えるSQLと調査の流れを順に紹介していきます。

この記事のポイント

・performance_schema は MySQL 8.0 以降でデフォルト有効で追加インストール不要
・events_statements_summary_by_digest で実行回数・平均実行時間が上位のSQLを特定できる
・events_waits と threads テーブルでロック待ちとスレッド滞留状態を調査できる
・スロークエリログは過去の記録、performance_schema は現在の状態を確認するための使い分けが大切


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

MySQL performance_schema とは何か

performance_schema は、MySQL 5.5 から導入されたパフォーマンス計測用の特殊なデータベースです。MySQL 8.0 以降はデフォルトで有効になっており、追加のインストールや設定なしにすぐ使い始められます。

仕組みとしては、MySQL がクエリを実行したり接続を処理したりするたびに、その実行時間・待機状態・ロック情報などを performance_schema の各テーブルにメモリ上で自動記録します。これを参照することで、「今何が遅いのか」「どこで待ちが発生しているのか」を MySQL の内側から直接調べられます。

主なテーブルの役割は次のとおりです。

・events_statements_summary_by_digest:SQLのパターン(ダイジェスト)ごとに実行回数・平均実行時間・検索行数を集計
・events_statements_current:現在実行中のクエリのリアルタイム情報
・events_waits_summary_global_by_event_name:待機イベントの種類ごとの累計を集計
・threads:接続中スレッドの状態(待機中・実行中・ロック待ちなど)
・setup_consumers:データ収集先の有効・無効を制御する設定テーブル

information_schema とは違い、performance_schema は「起動から現在までの動作記録」を持ちます。MySQL を再起動すると多くのテーブルはリセットされるため、調査は再起動前に行うのが基本です。

20年以上にわたって Linux サーバー上で MySQL・MariaDB を運用してきた経験から言うと、スロークエリログと performance_schema の2つを組み合わせることで、「過去の傾向」と「今の状態」の両方を把握できる調査体制が整います。

performance_schema が有効かどうかを確認する

1. 有効化の状態を確認する

まず、performance_schema が有効になっているか確認します。MySQL に接続してから次のクエリを実行してください。

SHOW VARIABLES LIKE 'performance_schema';

出力例(環境により異なります):

+--------------------+-------+ | Variable_name | Value | +--------------------+-------+ | performance_schema | ON | +--------------------+-------+

「ON」であれば有効です。MySQL 8.0 以降の標準バイナリではデフォルトが ON になっています。

2. 無効になっている場合の有効化

「OFF」になっている場合は、my.cnf(または my.ini)に次の行を追加して MySQL を再起動します。

# /etc/my.cnf または /etc/mysql/mysql.conf.d/mysqld.cnf を編集 [mysqld] performance_schema = ON

設定を反映するには再起動が必要です。

# RHEL 9系の場合 systemctl restart mysqld # Ubuntu 24.04 の場合 systemctl restart mysql

performance_schema の有効化はコンパイル時オプションにも依存しますが、公式バイナリを使っている限り有効にできます。

遅いクエリを performance_schema で特定する

1. 実行回数・実行時間で上位のクエリを確認する

events_statements_summary_by_digest は、SQL のパターン(ダイジェスト)ごとに実行回数や平均実行時間を集計しています。まずここを見ることで、「何度も実行されていて遅いSQL」を絞り込めます。

SELECT SCHEMA_NAME AS データベース名, LEFT(DIGEST_TEXT, 80) AS SQL概要, COUNT_STAR AS 実行回数, ROUND(AVG_TIMER_WAIT / 1000000000000, 3) AS 平均実行時間sec, ROUND(MAX_TIMER_WAIT / 1000000000000, 3) AS 最大実行時間sec, ROUND(SUM_ROWS_EXAMINED / COUNT_STAR, 0) AS 平均検索行数 FROM performance_schema.events_statements_summary_by_digest WHERE SCHEMA_NAME IS NOT NULL AND COUNT_STAR > 10 ORDER BY AVG_TIMER_WAIT DESC LIMIT 10;

出力例(環境により異なります):

+---------------+-----------------------------------+-----------+-----------------+------------------+--------------+ | データベース名 | SQL概要 | 実行回数 | 平均実行時間sec | 最大実行時間sec | 平均検索行数 | +---------------+-----------------------------------+-----------+-----------------+------------------+--------------+ | myapp | SELECT * FROM orders WHERE user...| 1420 | 2.345 | 8.901 | 980340 | | myapp | UPDATE sessions SET updated_at... | 8203 | 0.231 | 1.202 | 1 | +---------------+-----------------------------------+-----------+-----------------+------------------+--------------+

注目するのは「平均検索行数」です。実際に返している行数に対して検索行数が極端に多い場合は、インデックスが効いていない可能性があります。該当する SQL を特定したら、EXPLAIN で実行計画を確認するのが次のステップです。

「平均実行時間sec」が大きく「実行回数」も多い SQL が最優先の改善候補です。平均は低くても「最大実行時間sec」が突出している場合は、特定の条件やデータ量のときだけ遅くなるクエリが疑われます。

2. 実行中のクエリをリアルタイムで確認する

今まさに実行されているクエリは events_statements_current で確認できます。DB が重くなっている最中に調査するときに有効です。

SELECT t.PROCESSLIST_ID AS プロセスID, t.PROCESSLIST_DB AS データベース, t.PROCESSLIST_STATE AS 状態, ROUND(e.TIMER_WAIT / 1000000000000, 2) AS 実行時間sec, LEFT(e.SQL_TEXT, 100) AS SQL概要 FROM performance_schema.events_statements_current e JOIN performance_schema.threads t ON e.THREAD_ID = t.THREAD_ID WHERE e.SQL_TEXT IS NOT NULL ORDER BY e.TIMER_WAIT DESC LIMIT 20;

「実行時間sec」が大きい行が詰まっているクエリです。PROCESSLIST_ID を使えば KILL で強制終了することもできます。

3. 統計をリセットして現状を測り直す

サーバーの起動から時間が経っている場合、古いデータが混じっています。リセットしてから再計測すると、今の状況を正確に把握しやすくなります。

# 累計統計をリセット(MySQL サービスの再起動は不要) TRUNCATE TABLE performance_schema.events_statements_summary_by_digest;

リセット後は数分動作させてから再度クエリを実行すると、直近の傾向だけを確認できます。

ロック待ちと待機状態を調査する

1. 待機イベントの集計を確認する

遅いクエリが見当たらないのに DB が重い場合、ロック待ちや I/O 待ちが原因であることがあります。events_waits_summary_global_by_event_name を見ると、どの種類の待機が多いかが分かります。

SELECT EVENT_NAME AS 待機イベント名, COUNT_STAR AS 発生回数, ROUND(AVG_TIMER_WAIT / 1000000, 2) AS 平均待機マイクロ秒, ROUND(SUM_TIMER_WAIT / 1000000000000, 3) AS 合計待機sec FROM performance_schema.events_waits_summary_global_by_event_name WHERE COUNT_STAR > 0 AND EVENT_NAME NOT LIKE 'idle%' ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;

注目すべき待機イベントの種類は次のとおりです。

・wait/synch/mutex/innodb/:InnoDB の内部ロック。頻度が高ければ InnoDB の競合が疑われる
・wait/io/file/innodb/:InnoDB のファイル I/O 待ち。ディスク速度の問題や innodb_buffer_pool_size の不足が考えられる
・wait/lock/table/:テーブルロック待ち。MyISAM や DDL 実行中に発生しやすい
・wait/synch/rwlock/:読み書きロック。並列読み込みが多い環境で発生する

2. threads テーブルでスレッドの状態を確認する

今まさに待ちが発生しているスレッドを調べるには threads テーブルが便利です。

SELECT PROCESSLIST_ID AS プロセスID, PROCESSLIST_USER AS ユーザー, PROCESSLIST_DB AS データベース, PROCESSLIST_STATE AS 状態, PROCESSLIST_TIME AS 経過時間sec, LEFT(PROCESSLIST_INFO, 80) AS 実行中SQL FROM performance_schema.threads WHERE TYPE = 'FOREGROUND' AND PROCESSLIST_STATE IS NOT NULL AND PROCESSLIST_STATE NOT IN ('', 'Sleep') ORDER BY PROCESSLIST_TIME DESC LIMIT 20;

PROCESSLIST_STATE に「Waiting for table metadata lock」「Waiting for lock」などが表示されている場合はロック競合が起きています。対象の PROCESSLIST_ID を確認し、原因となっているセッションを KILL するかどうか判断します。

「経過時間sec」が大きいのに状態が「Sleep」以外のスレッドが複数ある場合は、単一の長時間クエリが他のクエリをブロックしている典型的なパターンです。

スロークエリログと performance_schema の使い分け

スロークエリログと performance_schema は、どちらも遅いクエリを調査するためのツールですが、役割が異なります。

・スロークエリログ:過去に遅かったクエリをファイルに記録する。MySQL の再起動後でも残る。しきい値(long_query_time)を超えたクエリだけが対象
・performance_schema:現在の実行状態をメモリ上で記録する。再起動でリセットされる。しきい値に関わらずすべてのクエリが集計対象

定常的に遅いクエリの傾向を把握するにはスロークエリログを使い、「今なぜ重いのか」をリアルタイムで調査するには performance_schema を見るのが効率的です。

Linux サーバー上での自前運用では、両方を有効にしておき状況に応じて使い分けるのが現場での基本的な考え方です。MySQLのパフォーマンス調査の全体像や実践的な運用ノウハウについては、Linux Master Pro Seminarのカリキュラムでも体系的に扱っています。

「データが蓄積されない・集計が空」への対処

performance_schema のテーブルを見ても集計が空、または DIGEST が NULL の行しかない場合は、次の2点を確認します。

1. コンシューマーの有効化状態を確認する

performance_schema は「インストゥルメント(計測対象)」と「コンシューマー(収集先)」の2段階で制御されています。コンシューマーが無効になっていると、テーブルにデータが蓄積されません。

-- コンシューマーの有効化状態を確認 SELECT NAME, ENABLED FROM performance_schema.setup_consumers WHERE NAME LIKE '%statements%';

出力例(環境により異なります):

+---------------------------------+---------+ | NAME | ENABLED | +---------------------------------+---------+ | events_statements_current | YES | | events_statements_history | YES | | events_statements_history_long | NO | +---------------------------------+---------+

ENABLED が「NO」になっているコンシューマーを有効にする場合は次のコマンドで変更できます(再起動で元に戻ります)。

UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_statements_history_long';

2. DIGEST が NULL になる理由と対処

events_statements_summary_by_digest の DIGEST_TEXT が NULL の行は、ダイジェストのキャッシュ(performance_schema_digests_size)が上限に達した場合に記録されます。この NULL 行は「キャッシュからあふれた SQL の集計」を意味しています。

解消するには、my.cnf で次の値を大きくして再起動します。

# /etc/my.cnf の [mysqld] セクション # デフォルト 10000。SQL の種類が多い環境では増やす performance_schema_digests_size = 20000

NULL 行の割合が多い場合は、この値を段階的に増やしながら調整します。

本記事のまとめ

performance_schema を使ったパフォーマンス調査の主要な SQL をまとめます。

やりたいこと 使うテーブル・コマンド
実行回数・実行時間が上位のSQLを特定 SELECT ... FROM events_statements_summary_by_digest ORDER BY AVG_TIMER_WAIT DESC
現在実行中のクエリを確認 SELECT ... FROM events_statements_current JOIN threads WHERE SQL_TEXT IS NOT NULL
待機イベントの種類と頻度を集計 SELECT ... FROM events_waits_summary_global_by_event_name ORDER BY SUM_TIMER_WAIT DESC
ロック待ちのスレッドを探す SELECT ... FROM threads WHERE PROCESSLIST_STATE IS NOT NULL ORDER BY PROCESSLIST_TIME DESC
統計をリセットして現状を測り直す TRUNCATE TABLE events_statements_summary_by_digest
コンシューマーの有効化状態を確認 SELECT NAME, ENABLED FROM setup_consumers WHERE NAME LIKE '%statements%'
performance_schema を使えば、スロークエリログだけでは掴めない「今の重さの原因」を MySQL の内側から特定できます。再起動後はデータがリセットされるため、問題が発生したときに素早く調査できるよう、基本的な SQL を手元に保管しておくと役立ちます。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、

MySQL・MariaDB などデータベースサーバーの構築・運用から、performance_schema を使ったパフォーマンス調査の実践まで、Linux Master Pro Seminar では少人数ハンズオン形式で体系的に学べます。

>> セミナー詳細・日程確認はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。