MySQLはOracle DatabaseやPostgreSQLなどのデータベースと違い、シングルプロセス・マルチスレッド型のデータベースです。Oracle DatabaseやPostgreSQLは、マルチプロセス型のデータベースです。このマルチプロセス型の場合は、OSのコマンドps
等を使用することで、内部プロセスやユーザ接続プロセスなど実行されているプロセス名がわかります。
MySQLは、OSコマンドからでは1つのプロセスとして表示されるため内部スレッドを特定することは難しく、MySQLのテーブルやSHOW構文から確認することになります。今回はperformance_schemaのthreadsテーブルを使用して、MySQLの内部スレッドやユーザ接続スレッドを見てみましょう。
MySQLのスレッドの確認方法
一般的にMySQLのスレッド状態は、SHOW PROCESSLIST
で表示することが多いと思います。このコマンドでわかるのは、フォアグラウンドで実行されているスレッドです。たとえば、ユーザ接続スレッドやIO_threadなどです。このSHOW PROCESSLIST
とinformation_schema.processlist
テーブルは、同等のものを表示します。
運用ではこれらが示す情報が分かれば十分なのですが、MySQLでは複数の内部スレッドがバックグラウンドで稼働しており(以降、バックグラウンドスレッド)、performance_schema
のthreads
テーブルはフォアグラウンドスレッドとバッググラウンドスレッド両方を確認することができます。バックグラウンドスレッドにはパージスレッドやページクリーナスレッドなどのInnoDB関連のスレッドがあります。
そして、SHOW PROCESSLIST
は実行するとmutexロックを取得するため高負荷時は結果がなかなか返ってこないこともありますが、threads
テーブルへのアクセスはmutexロックを取得しないのでパフォーマンスの影響は少なくなります。
threadsテーブル
MySQL5.7.17を使用して、threadsテーブルカラムを見てみます。以下がカラムの情報です。
カラム名 | 説明 |
THREAD_ID | 一意のスレッド識別子 |
NAME | 実行している処理 |
TYPE | FOREGROUND または BACKGROUND |
PROCESSLIST_ID | SHOW PROCESSLIST のId カラムに表示される値 |
PROCESSLIST_USER | 接続元のユーザー名 |
PROCESSLIST_HOST | 接続元のホスト名 |
PROCESSLIST_DB | スレッドのデフォルトのデータベース |
PROCESSLIST_COMMAND | 実行しているコマンドの種類 |
PROCESSLIST_TIME | 現在の状態になってからの秒数 |
PROCESSLIST_STATE | SHOW PROCESSLIST のState カラムに表示される値 |
PROCESSLIST_INFO | 実行しているステートメント |
PARENT_THREAD_ID | 親スレッドID |
ROLE | 未使用 |
INSTRUMENTED | スレッドが統計情報を取得されるかどうか(YES/NO) |
HISTORY | スレッドの履歴イベントを記録するかどうか(YES/NO) |
CONNECTION_TYPE | TCP/IPやSocketなどの接続タイプ |
THREAD_OS_ID | OSで管理されているスレッドID |
threadsテーブルの中身を見てみましょう。
NAME
カラムは実行している処理を表し、'/' 文字で区切られた一連のコンポーネントから構成されます。たとえば、thread/sql/slave_io
はIO_thread、thread/sql/slave_sql
はsql_threadでthread/sql/one_connection
はユーザ接続スレッドです。
PROCESSLIST_*
のカラムはSHOW PROCESSLIST
に表示されている内容で、フォアグラウンドスレッドの行には値がありますが、バックグラウンドスレッドは基本的にNULLが多いです。
SHOW PROCESSLIST
のId
カラムはTHREAD_ID
カラムではなく、PROCESSLIST_ID
カラムと紐付いています。
INSTRUMENTED
カラムとHISTORY
カラムはフォアグラウンドスレッドの場合、setup_actors
テーブルのユーザー名とホスト名に一致していればYESとなり、バックグラウンドプロセスの場合はデフォルトYESです。
setup_actors
テーブルはフォアグラウンドスレッドのモニタリングを有効にしたいユーザー名とホスト名を格納します。デフォルトではすべてのユーザ名とホスト名に対して有効です。
また、HISTORY
カラムとCONNECTION_TYPE
カラムはMySQL5.7.8から、THREAD_OS_ID
カラムはMySQL5.7.9から追加されました。
gdbを使用して特定のスレッドにアタッチする
gdbとはLinuxで使用されるデバッガです。さまざまな言語に対してプロセスの状態を見たり、変数の中身を確認することができます。MySQLではクラッシュ時に吐かれるコアファイルの分析に用いたり、稼働中のMySQLのPIDを指定して実行することでMySQLにアタッチします。アタッチするとMySQLはgdbによって制御させるため実行が停止します。デタッチすることで実行が開始されます。
そして、threads
テーブルにMySQL5.7.9から追加されたTHREAD_OS_ID
カラムの値を使用して特定のスレッドのみに対してアタッチすることもできます。たとえば、sql_threadにアタッチしてみます。ただし、この内容は本番環境では実行しないでください。あくまでも検証を目的としたものです。
sql_threadのOSIDを確認する
GTIDを使用したマスタースレーブ構成のMySQLを用意して、スレーブに対して実行します。前述の通り、NAMEカラムがthread/sql/slave_sql
のものがsql_threadなので、条件を指定してTHREAD_OS_ID
を取得します。
IDが142770であることがわかりました。そのIDに対してgdbを実行します。
この状態でsql_threadのみ実行が停止しています。マスターに対して更新をかけ、スレーブのSHOW SLAVE STATUS
を確認してみます。
このように、Slave_SQL_Running
はYESでSeconds_Behind_Master
は0のため問題ないように思えますが、Retrieved_Gtid_Set
は1-303と進んでもExecuted_Gtid_Set
は1-4と、適用されていないことがわかります。このように特定のスレッドの実行を停止させることが可能です。sql_threadはデタッチすることで実行が再開されます。ちなみに、sql_threadを停止させるにはSTOP SLAVE SQL_THREAD
コマンドで可能なため、このような方法は使うことないと思います。
まとめ
今回は、performance_schemaのthreadsテーブルとthreadsテーブルからIDを取得して、gdbで特定のスレッドにアタッチする方法について紹介しました。実運用では、SHOW PROCESSLIST
の結果が返ってこなかったり、詳細なスレッド情報を確認したい場合などは、このテーブルを参照すれば良いでしょう。