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はタイムラインの自動再生を実現する

ブログ    

推薦する

海外の無料写真素材サイトベスト9

良い画像素材のウェブサイトを見つけるのは難しいです。特に無料です。良い写真には非常に目を引く視覚効果...

Vue プロジェクトで mock.js を使用するための完全な手順

Vue プロジェクトで mock.js を使用する開発ツールの選択: Vscode 1. コマンドラ...

プレーンな JS オブジェクトの代わりに Map を使用する場合

目次1. マップは任意のタイプのキーを受け入れます2. マップにはキー名に関する制限はありません3....

HTML の相対パスと絶対パスの違いの分析

HTML 初心者は、ファイルを正しく参照する方法という問題によく遭遇します。たとえば、HTML ペー...

MySQL 8.0 のインデックス スキップ スキャン

序文MySQL 8.0.13 では、インデックス スキップ スキャン (インデックス ジャンプ スキ...

10 種類のモダンなレイアウトを実現するための CSS コード

序文日曜日に自宅で web.dev の 2020 3 日間ライブを視聴したところ、興味深い点がたくさ...

なぜCSSをヘッドタグに配置する必要があるのか

考えてみてください。なぜcss 、 javascriptのようにbodyタグの末尾ではなく、 hea...

div を下から上にスライドさせる CSS3 の例

1. まず、CSS3 のターゲット セレクターを使用し、a タグを使用して id セレクターを指定し...

JavaScript イベントバブリング、イベントキャプチャ、イベント委任の詳細な説明

1. イベントバブリング: JavaScript イベント伝播のプロセスでは、要素でイベントがトリガ...

MySQLリモートアクセスの設定方法をステップバイステップで説明します

序文MySQL データベースを使用する場合、クライアントはデータベース サーバーにリクエストを送信す...

Apache FlinkCEP でタイムアウトステータス監視を実装するための詳細な手順

CEP - 複合イベント処理。ご注文後、一定期間内にお支払いの確認が取れませんでした。タクシーの配...

CSS3で実装されたスライドメニュー

結果:実装コード: <!DOCTYPE html><html class=&quo...

CSS3 を使用した背景ぼかし効果の 3 つの例

導入から始めず、いきなり本題に入りましょう。通常の背景ぼかし効果は次のとおりです。 プロパティを使用...

Vueはドラッグプログレスバーを実装します

この記事では、ドラッグプログレスバーを実現するためのVueの具体的なコードを例として紹介します。具体...

MySQLのレプリケーションの詳細な分析

1.MySQLレプリケーションの概念これは、プライマリ データベースの DDL および DML 操作...