MySQL道普請便り

第242回MySQLのデッドロックに関するパラメーターとステータス

第239回 SHOW ENGINE INNODB STATUSのLATEST DETECTED DEADLOCKの見方の記事にて、SHOW ENGINE INNODB STATUSに表示される直近のデッドロックの見方を紹介しました。

この記事では、SHOW ENGINE INNODB STATUSでは直近に発生したデッドロックの1件のみを確認することが可能と説明しました。では、過去に発生したデッドロックを確認するにはどうしたらいいでしょうか。今回はデッドロックが発生した時点でログに落とす方法や、その他デッドロックに関するパラメーターやステータスについて紹介したいと思います。MySQLのLTS最新バージョンであるMySQL 8.4.4を利用します。

デッドロックに関するパラメーター

innodb_print_all_deadlocksパラメーター

はじめに、innodb_print_all_deadlocksパラメーターです。こちらをONにするとデッドロックが発生し、InnoDBがそれを検知すると、デッドロックに関する情報がエラーログに出力されます。このパラメーターはデフォルトはOFFになっています。そのため、すべてのデッドロックに関する情報を収集または確認したい際は、このパラメーターをONにしてください。以下、出力されたサンプルになります。SHOW ENGINE INNODB STATUSのLATEST DETECTED DEADLOCKの出力と近い内容がエラーログに出力されます。

# cat mysqld.err
<snip>
TRANSACTION 1363, ACTIVE 26 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 2
MySQL thread id 9, OS thread handle 6196981760, query id 156 localhost msandbox updating
update t set id=9 where id=2
RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `test`.`t` trx id 1363 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 000000000553; asc      S;;
 2: len 7; hex 01000001220151; asc     " Q;;

RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `test`.`t` trx id 1363 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000000000554; asc      T;;
 2: len 7; hex 020000011d0151; asc       Q;;

TRANSACTION 1364, ACTIVE 13 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 2
MySQL thread id 10, OS thread handle 6198095872, query id 157 localhost msandbox updating
update t set id=2 where id=1
RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `test`.`t` trx id 1364 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000000000554; asc      T;;
 2: len 7; hex 020000011d0151; asc       Q;;

RECORD LOCKS space id 3 page no 4 n bits 72 index PRIMARY of table `test`.`t` trx id 1364 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 000000000553; asc      S;;
 2: len 7; hex 01000001220151; asc     " Q;;

innodb_deadlock_detectパラメーター

このパラメーターを変更することで、InnoDBのデッドロック検知を無効化することができます。デフォルトはONで、デッドロックの検知が有効になっています。有効になっていると、InnoDBはトランザクション間でデッドロックの発生を自動で検知して、それを解消するためにいずれかのトランザクションをロールバックします。無効にするとデッドロックの検知が行われず、デッドロックが発生してもトランザクションは待機したままになります。

その場合、デッドロックの解消はinnodb_lock_wait_timeout設定に依存し、タイムアウトされるまで待機します。デッドロック検知は同時実行性が高く、多数のトランザクションが同じロックを待機している場合にスループットが低下する可能性があります。それを回避する手段として、デッドロック検知を無効化するアプローチがありました。

MySQL 8.0.17とそれ以前は、トランザクションスレッドがデッドロックの検知を行っていました。そのため、高い同時実行性を持つMySQL環境では、この検知処理がリソースを消費し、結果としてCPU利用率が高くなることがありました。しかし、MySQL 8.0.18とそれ以降はデッドロック検知がバックグラウンドスレッドで行われるようになり、リソースを大幅に消費することなく検知が可能になりました。したがって、MySQL 8.0.18とそれ以降を利用している場合は、このパラメーターをOFFにする必要性は低くなったと考えられます。

デッドロックを検出する専用のバックグラウンドスレッドは、performance_schemaのthreadsテーブルから確認できます。

mysql> SELECT NAME,TYPE FROM threads WHERE NAME LIKE '%/srv_lock_timeout_thread';
+---------------------------------------+------------+
| NAME                                  | TYPE       |
+---------------------------------------+------------+
| thread/innodb/srv_lock_timeout_thread | BACKGROUND |
+---------------------------------------+------------+
1 row in set (0.00 sec)

デッドロックに関するステータス

SHOW ENGINE INNODB STATUS

第239回 SHOW ENGINE INNODB STATUSのLATEST DETECTED DEADLOCKの見方の記事にて紹介しています。直近に発生したデッドロックの1件のみが確認可能です。詳しくは記事をご参照ください。

information_schema.INNODB_METRICSテーブル

続いて、INNODB_METRICSテーブルです。INNODB_METRICSテーブルでは、InnoDBに関するパフォーマンスやリソース情報を提供します。INNODB_METRICSテーブルの項目の1つにlock_deadlocksがあります。この項目では、mysqldが起動してからこれまでに発生したデッドロックの累積値を確認することができます。COUNTカラムの値がデッドロックの累積値になります。MySQLサーバーを再起動すると、カウンターはゼロにリセットされます。

mysql> SELECT NAME,COUNT,STATUS,COMMENT FROM INNODB_METRICS WHERE NAME ='lock_deadlocks';
+----------------+-------+---------+---------------------+
| NAME           | COUNT | STATUS  | COMMENT             |
+----------------+-------+---------+---------------------+
| lock_deadlocks |     1 | enabled | Number of deadlocks |
+----------------+-------+---------+---------------------+
1 row in set (0.00 sec)

INNODB_METRICSテーブルのいくつかのカウンターはデフォルトでは無効になっていますが、デッドロックのカウンターはデフォルトで有効になっているので、設定変更せずとも確認することができます。STATUSカラムの値がenabledであれば、そのカウンターは有効です。

まとめ

今回はデッドロックに関するパラメーターやステータスについて紹介しました。

デッドロックが頻繁に発生して問題となる場合は、すべてのデッドロックに関する情報を出力するために、innodb_print_all_deadlocksパラメーターをONに変更しましょう。ONにせずとも、日々予防としてinformation_schema.INNODB_METRICSテーブルから件数を確認したり、SHOW ENGINE INNODB STATUSから直近のデッドロック情報を確認するだけでも良いでしょう。

今回は以下のドキュメントを参考にしましたので、あわせてご確認ください。

おすすめ記事

記事・ニュース一覧