MySQLデッドロック問題の詳細な分析

MySQLデッドロック問題の詳細な分析

序文

私たちのビジネスがまだ初期段階にあり、同時実行の度合いが比較的低い場合、数年間はデッドロックの問題に遭遇しないかもしれません。逆に、私たちのビジネスの同時実行の度合いが非常に高い場合、時々発生するデッドロックの問題は間違いなく私たちを非常に困惑させるでしょう。しかし、デッドロック問題が発生すると、多くの経験の浅い学生の最初の反応はダチョウになることです。「これは非常に高度な問題で、理解できません。運命に任せましょう。いつも起こるわけではありません。」実際、MySQL のステートメント ロックの分析に関する以前に書いた 3 つの記事と、デッドロック ログの分析に関するこの記事を注意深く読めば、デッドロック問題の解決はそれほど混乱することはないはずです。

準備

ストーリーがスムーズに展開するためには、表を作成する必要があります。

テーブルの作成ヒーロー(
 id INT、
 名前 VARCHAR(100)、
 国varchar(100)、
 主キー (id)、
 KEY idx_name (名前)
) エンジン=InnoDB CHARSET=utf8;

hero テーブルの id 列にクラスター化インデックスを作成し、name 列にセカンダリ インデックスを作成しました。この英雄テーブルは主に三国時代の英雄を保存するために使用されます。テーブルにいくつかのレコードを挿入します。

ヒーロー値に挿入
 (1、「劉備」、「蜀」)、
 (3、「諸葛亮」、「蜀」)、
 (8、「曹操」、「魏」)、
 (15、「x荀玉」、「魏」)、
 (20、「孫権」、「呉」)

テーブル内のデータは次のようになります。

mysql> SELECT * FROM hero;
+----+------------+---------+
| ID | 名前 | 国 |
+----+------------+---------+
| 1 | l劉備| 蜀|
| 3 | 諸葛亮 | 蜀 |
| 8 | c曹操 | 魏 |
| 15 | xXun Yu | 魏 |
| 20 | s孫権 | 呉 |
+----+------------+---------+
セット内の行数は 5 です (0.00 秒)

準備は完了です。

デッドロックシナリオの作成

まず、デッドロック シナリオを作成し、セッション A とセッション B でそれぞれ 2 つのトランザクションを実行してみましょう。具体的な状況は次のとおりです。

分析してみましょう:

  • ステップ ③ からわかるように、セッション A のトランザクションは、まず、ヒーロー テーブルのクラスター化インデックス内の ID 値 1 のレコードに X タイプのポジティブ レコード ロックを追加します。
  • ステップ ④ からわかるように、セッション B のトランザクションは、ヒーロー テーブルのクラスター化インデックス内の ID 値 3 のレコードに X タイプのポジティブ レコード ロックを追加します。
  • ステップ 5 からわかるように、セッション A のトランザクションは、ヒーロー テーブルのクラスター化インデックス内の ID 値 3 のレコードに X タイプの通常のレコード ロックを追加しようとしますが、ステップ 4 でセッション B のトランザクションによって追加されたロックと競合するため、セッション A はブロック状態になり、ロックの取得を待機します。
  • ステップ 6 からわかるように、セッション B のトランザクションは、hero テーブルのクラスター化インデックス内の ID 値 1 のレコードに X タイプのポジティブ レコード ロックを追加しようとしていますが、ステップ 3 でセッション A のトランザクションによって追加されたロックと競合しています。この時点で、セッション A とセッション B のトランザクションは、お互いが保持しているロックを周期的に待機しており、デッドロックが発生します。MySQL サーバーのデッドロック検出メカニズムがこれを検出すると、ロールバックするトランザクションが選択され、クライアントにメッセージが送信されます。

エラー 1213 (40001): ロックを取得しようとしたときにデッドロックが見つかりました。トランザクションを再起動してください。

上記は、ステートメントにどのようなロックが追加されているかという観点からのデッドロック分析です。しかし、実際のアプリケーションでは、どのステートメントがデッドロックを引き起こしたのかまったくわからない場合があります。デッドロックが発生したときに MySQL によって生成されるデッドロック ログに基づいて、どのステートメントがデッドロックを引き起こしたかを逆に特定し、ビジネスを最適化する必要があります。

デッドロックログを表示

InnoDB を設計した人が、デッドロックが最後に発生したときのシステムのロック状態など、InnoDB ストレージ エンジンに関する状態情報を表示するための SHOW ENGINE INNODB STATUS コマンドを提供してくれました。上記の例のデッドロックが発生した場合、次のコマンドを実行します。

mysql> エンジン INNODB ステータスを表示\G
...その他多くの情報は省略されています------------------------
最近検出されたデッドロック
------------------------
2019-06-20 13:39:19 0x70000697e000
*** (1)取引:
トランザクション 30477、アクティブ 10 秒開始インデックス読み取り
使用中の MySQL テーブル 1、ロックされているテーブル 1
LOCK WAIT 3 つのロック構造体、ヒープ サイズ 1160、2 つの行ロック
MySQL スレッド ID 2、OS スレッド ハンドル 123145412648960、クエリ ID 46 localhost 127.0.0.1 ルート統計
更新のために、id = 3 のヒーローから * を選択します
*** (1) このロックが許可されるのを待機しています:
レコード ロック スペース ID 171 ページ番号 3 n ビット 72 インデックス PRIMARY テーブル `dahaizi`.`hero` trx ID 30477 lock_mode X はレコードをロックしますが、ギャップ待機はロックしません
レコード ロック、ヒープ番号 3 物理レコード: n_fields 5; コンパクト フォーマット; 情報ビット 0
 0: 長さ 4; 16 進数 80000003; 昇順 ;;
 1: 長さ 6; 16 進数 000000007517; asc u ;;
 2: 長さ 7; 16 進数 80000001d0011d; 昇順 ;;
 3: 長さ 10; 16 進数 7ae8afb8e8919be4baae; asc z ;;
 4: 長さ 3; 16 進数 e89c80; 昇順 ;;

*** (2)取引:
トランザクション 30478、アクティブ 8 秒開始インデックス読み取り
使用中の MySQL テーブル 1、ロックされているテーブル 1
3 つのロック構造体、ヒープ サイズ 1160、2 つの行ロック
MySQL スレッド ID 3、OS スレッド ハンドル 123145412927488、クエリ ID 47、localhost 127.0.0.1、ルート統計
更新のために、id = 1 のヒーローから * を選択します
*** (2) ロックを保持する:
レコード ロック スペース ID 171 ページ番号 3 n ビット 72 インデックス PRIMARY テーブル `dahaizi`.`hero` trx ID 30478 lock_mode X はレコードをロックしますが、ギャップはロックしません
レコード ロック、ヒープ番号 3 物理レコード: n_fields 5; コンパクト フォーマット; 情報ビット 0
 0: 長さ 4; 16 進数 80000003; 昇順 ;;
 1: 長さ 6; 16 進数 000000007517; asc u ;;
 2: 長さ 7; 16 進数 80000001d0011d; 昇順 ;;
 3: 長さ 10; 16 進数 7ae8afb8e8919be4baae; asc z ;;
 4: 長さ 3; 16 進数 e89c80; 昇順 ;;

*** (2) このロックが許可されるのを待機しています:
レコード ロック スペース ID 171 ページ番号 3 n ビット 72 インデックス PRIMARY テーブル `dahaizi`.`hero` trx ID 30478 lock_mode X はレコードをロックしますが、ギャップ待機はロックしません
レコード ロック、ヒープ番号 2 物理レコード: n_fields 5、コンパクト フォーマット、情報ビット 0
 0: 長さ 4; 16 進数 80000001; 昇順 ;;
 1: 長さ 6; 16 進数 000000007517; asc u ;;
 2: 長さ 7; 16 進数 80000001d00110; 昇順 ;;
 3: 長さ 7; 16 進数 6ce58898e5a487; asc l ;;
 4: 長さ 3; 16 進数 e89c80; 昇順 ;;

*** トランザクションをロールバックします (2)
------------
...他の多くの情報は省略されています

最新のデッドロック情報のみを対象とするため、LATEST DETECTED DEADLOCK セクションを個別に分析します。デッドロック ログ出力が何を意味するのかを行ごとに見てみましょう。

まず最初の文を見てみましょう:

2019-06-20 13:39:19 0x70000697e000

この文は、デッドロックが発生した時刻が 2019-06-20 13:39:19 であり、それに続く 16 進数 0x70000697e000 の文字列が、オペレーティング システムによって現在のセッションに割り当てられたスレッドのスレッド ID を表すことを意味します。

次に、デッドロックが発生したときの最初のトランザクションに関する情報があります。

*** (1)取引:

# トランザクション ID は 30477 です。トランザクションは 10 秒間アクティブ状態になっています。トランザクションによって現在実行されている操作は、「インデックス読み取りを開始しています」です。
トランザクション 30477、アクティブ 10 秒開始インデックス読み取り

# このトランザクションは 1 つのテーブルを使用し、1 つのテーブルをロックします (これはテーブルにテーブル ロックが追加されることを意味するものではありません。テーブルが一貫して読み込まれない限り、ロックする必要があります。ロックの具体的な方法については、ロック ステートメントの分析またはブックレットの章を参照してください)
使用中の MySQL テーブル 1、ロックされているテーブル 1

# このトランザクションは LOCK WAIT 状態にあり、3 つのロック構造 (2 つの行ロック構造、1 つのテーブル レベルの X 型意図ロック構造、ロック構造につ​​いては小冊子で詳しく説明されています) があり、ヒープ サイズはロック構造を格納するために要求されるメモリ サイズです (無視できます)、2 つの行ロック構造があります LOCK WAIT 3 つのロック構造、ヒープ サイズ 1160、2 つの行ロック

# このトランザクションのスレッド ID は 2 (MySQL 独自のスレッド ID) です。オペレーティング システム レベルのスレッド ID は長い数字の文字列です。現在のクエリの ID は 46 (MySQL 内部使用、無視できます) で、ユーザー名とホスト情報もあります。MySQL スレッド ID 2、OS スレッド ハンドル 123145412648960、クエリ ID 46 localhost 127.0.0.1 ルート統計

# このトランザクションをブロックするステートメント select * from hero where id = 3 for update

# このトランザクションが現在取得を待機しているロック:
*** (1) このロックが許可されるのを待機しています:

# 取得するテーブルスペースIDは151、ページ番号は3、つまりヒーローテーブルのPRIMAYインデックスのレコードのロックです(n_bitsはこのページのロック情報を格納するために割り当てられたメモリスペースの文字列で、パンフレットに詳細が記載されています)。ロックタイプはXタイプのレコードですが、ギャップロックではありません。
レコード ロック スペース ID 171 ページ番号 3 n ビット 72 インデックス PRIMARY テーブル `dahaizi`.`hero` trx ID 30477 lock_mode X はレコードをロックしますが、ギャップ待機はロックしません

# ページ内のこのレコードの heap_no は 2 です。具体的なレコード情報は次のとおりです。
レコード ロック、ヒープ番号 3 物理レコード: n_fields 5; コンパクト フォーマット; 情報ビット 0

# これは主キー値 0 です: len 4; hex 80000003; asc ;;

# これは trx_id の隠し列 1: len 6; hex 000000007517; asc u ;;

# これは roll_pointer の隠し列 2: len 7; hex 80000001d0011d; asc ;;

# これは名前の列 3 です: len 10; hex 7ae8afb8e8919be4baae; asc z ;;

# これは国列 4 です: len 3; hex e89c80; asc ;;

この情報から、セッション A のトランザクションは 2 つのレコードのロック構造を生成しましたが、レコードの 1 つに対する X タイプの通常のレコード ロック (rec は取得されますが、gap は取得されません) が取得されなかったことがわかります。ロックされなかったレコードの場所は、テーブルスペース ID が 151、ページ番号が 3、heap_no が 2 です。もちろん、InnoDB を設計した人も、このレコードの詳細を親切に教えてくれました。主キー値は 80000003 で、これは実際に InnoDB の内部ストレージに使用される形式です。これは実際には数字の 3 を表しており、これはトランザクションが、ヒーロー テーブルのクラスター化インデックス内の主キー値が 3 であるレコードの X タイプの通常レコード ロックを取得するのを待機していることを意味します。

次に、デッドロックが発生したときの 2 番目のトランザクションに関する情報があります。

すでにほとんどの情報を紹介しているので、重要なものだけを取り上げます。

*** (2)取引:
トランザクション 30478、アクティブ 8 秒開始インデックス読み取り
使用中の MySQL テーブル 1、ロックされているテーブル 1
3 つのロック構造体、ヒープ サイズ 1160、2 つの行ロック
MySQL スレッド ID 3、OS スレッド ハンドル 123145412927488、クエリ ID 47 localhost 127.0.0.1 ルート統計
更新のために、id = 1 のヒーローから * を選択します

# トランザクションによって取得されたロック情報を示します*** (2) ロックを保持します:
レコード ロック スペース ID 171 ページ番号 3 n ビット 72 インデックス PRIMARY テーブル `dahaizi`.`hero` trx ID 30478 lock_mode X はレコードをロックしますが、ギャップはロックしません
レコード ロック、ヒープ番号 3 物理レコード: n_fields 5; コンパクト フォーマット; 情報ビット 0

# 主キーの値は3です
0: 長さ 4; 16 進数 80000003; 昇順 ;;
1: 長さ 6; 16 進数 000000007517; asc u ;;
2: 長さ 7; 16 進数 80000001d0011d; 昇順 ;;
3: 長さ 10; 16 進数 7ae8afb8e8919be4baae; asc z ;;
4: 長さ 3; 16 進数 e89c80; 昇順 ;;

# トランザクションが取得を待機しているロック情報を示します*** (2) このロックの許可を待機中:
レコード ロック スペース ID 171 ページ番号 3 n ビット 72 インデックス PRIMARY テーブル `dahaizi`.`hero` trx ID 30478 lock_mode X はレコードをロックしますが、ギャップ待機はロックしません
レコード ロック、ヒープ番号 2 物理レコード: n_fields 5、コンパクト フォーマット、情報ビット 0

# 主キーの値は1です
0: 長さ 4; 16 進数 80000001; 昇順 ;;
1: 長さ 6; 16 進数 000000007517; asc u ;;
2: 長さ 7; 16 進数 80000001d00110; 昇順 ;;
3: 長さ 7; 16 進数 6ce58898e5a487; asc l ;;
4: 長さ 3; 16 進数 e89c80; 昇順 ;;

上記の出力から、セッション B のトランザクションが、ヒーロー テーブルのクラスター化インデックス内の主キー値が 3 のレコードに対して X タイプの適切なレコード ロックを取得し、ヒーロー テーブルのクラスター化インデックス内の主キー値が 1 のレコードに対して X タイプの適切なレコード ロックを取得するのを待機していることがわかります (暗黙的な意味は、ヒーロー テーブルのクラスター化インデックス内の主キー値が 1 のレコードに対して X タイプの適切なレコード ロックがセッション A のトランザクションによって取得されたことです)。

最後の部分を見てください:

*** トランザクションをロールバックします (2)

最後に、InnoDB ストレージ エンジンは、セッション B のトランザクションである 2 番目のトランザクションをロールバックすることを決定します。

デッドロック分析のアイデア

1. デッドロック ログを確認するときは、まず、デッドロックしたトランザクションがロックを取得するためにどのステートメントを待機しているかを確認します。

この例では、SESSION A をブロックするステートメントは次のとおりです。

更新のために、id = 3 のヒーローから * を選択します

SESSION B がブロックされるステートメントは次のとおりです。

更新のために、id = 1 のヒーローから * を選択します

次に、独自のビジネス コード内でこれらの 2 つのステートメントが配置されているトランザクション内の他のステートメントを見つけることを忘れないでください。

2. デッドロックが発生したトランザクション内のすべてのステートメントを見つけたら、トランザクションによって取得されたロックと待機されているロックに関する情報を比較して、デッドロック プロセスを分析します。

デッドロック ログから、セッション A が、ヒーロー テーブルのクラスター化インデックスの ID 値 1 のレコードの X タイプの通常レコード ロックを取得していることがわかります (これは、実際にはセッション B が待機しているロックから取得されます)。セッション A のステートメントを見ると、次のステートメントが原因であることがわかります (ステートメント ロック分析に関する 3 つの記事を参照してください)。

更新のために、id = 1 の hero から * を選択します。

さらに、SESSION B は、hero テーブル内のクラスター化インデックス ID 値が 3 のレコードに対して、X タイプの通常のレコード ロックを取得します。SESSION B のステートメントを見ると、次のステートメントが原因であることがわかります (ステートメント ロック分析に関する 3 つの記事を参照)。

更新のために、id = 3 の hero から * を選択します。

次に、セッション A が、ヒーロー テーブル内のクラスター化インデックス ID 値が 3 であるレコードの X タイプの通常レコード ロックを待機していることがわかります。これは、次のステートメントによって発生します。

更新のために、id = 3 の hero から * を選択します。

次に、セッション B が、ヒーロー テーブル内のクラスター化インデックス ID 値が 1 であるレコードの X タイプの通常レコード ロックを待機していることがわかります。これは、次のステートメントによって発生します。

更新のために、id = 1 の hero から * を選択します。

その後、デッドロック ログに基づいて、デッドロック形成プロセス全体が復元されます。

要約する

以上がこの記事の全内容です。この記事の内容が皆様の勉強や仕事に何らかの参考学習価値をもたらすことを願います。123WORDPRESS.COM をご愛顧いただき、誠にありがとうございます。

以下もご興味があるかもしれません:
  • Mysql のデッドロックの表示とデッドロックの除去の詳細な説明
  • MySQLのデッドロックチェック処理の通常の方法
  • MySQLデッドロックの原因と解決策
  • MySQL デッドロック ルーチン: 一意のインデックスの下でのバッチ挿入順序の不一致
  • MySQL デッドロック シナリオ例の分析
  • 魔法のMySQLデッドロックトラブルシューティング記録
  • MySQL データベースのパージデッドロック問題の分析
  • SQLによる分散デッドロックの検出と排除の詳細な説明

<<:  Linux netfilter/iptables の知識ポイントの詳細な説明

>>:  JSはタイムラインの自動再生を実現する

推薦する

MySQL 5.7 でルートパスワードを忘れた後に変更する方法の詳細なチュートリアル

序文長い間、MySQL のアプリケーションおよび学習環境は MySQL 5.6 以前のバージョンであ...

Vue-CLI マルチページディレクトリパッケージ化手順の記録

ページディレクトリ構造 デフォルトの HTML テンプレート ファイル public/index.h...

Django 2.2 を MySQL データベースに接続する方法

1. プロジェクトの実行時に報告されるエラー情報は次のとおりです。 ファイル "/home...

Vueはキャンバスの手書き入力を使用して中国語を認識します

効果画像: 序文:最近、屋外の大画面プロジェクトに取り組んでいました。システムの入力方法は使いにくか...

Vue ElementUI で Excel ファイルを手動でサーバーにアップロードする方法の詳細な説明

目次概要プロパティ設定処理ロジック概要具体的な需要シナリオは次のとおりです。 Excel ファイルを...

Linux スクリプトの基礎を詳しく紹介

目次1. スクリプトvim環境2. シェルスクリプトで環境を定義する方法3. シェルスクリプト内の翻...

HTML の一般的でないタグ optgroup、sub、sup、bdo のサンプルコード

Optgroup は、ドロップダウン リストのコンテンツをより整理するために、select タグで使...

Ubuntuサーバーの一般的なコマンドの概要

以下のコマンドのほとんどは、コンソール/ターミナル/シェルで入力する必要があります。 'su...

フロントエンド例外 502 不正なゲートウェイの原因と解決策

目次502 不正なゲートウェイ エラーの発生1. 502 不正なゲートウェイ エラーとは何ですか? ...

Windows 10 で MySQL を完全にアンインストールして再インストールするための詳細な手順

さまざまな理由で、誰もが MySQL を再インストールする必要があると思います。 MySQL と Q...

MySQLにインデックスを追加しても効果がないいくつかの状況について簡単に説明します。

インデックスを追加すると、クエリの効率が向上します。インデックスを追加するということは、ドキュメント...

HTML でフレームセット タグを使用するチュートリアル

フレームセット ページは通常の Web ページとは多少異なります。依然として <HTML>...

Windows 10 システムに mysql-8.0.13 (zip インストール) をインストールする詳細なチュートリアル

インストール環境の説明•システムバージョン: windows10 •MySQL バージョン: mys...

MySQL 5.6.23 のインストールと設定環境変数のチュートリアル

この記事では、MySQL 5.6.23のインストールと設定のチュートリアルを参考までに紹介します。具...

W3C 検証に合格するにはどうすればいいですか?

W3C では、さまざまなタグの規定を設定するだけでなく、Web ページの作成者が実際に W3C 規...