障害分析 | 挿入同時実行デッドロックからの挿入ロック ソース コード ロジックの分析

作者: 李希超

笑うのが大好きな江蘇省蘇寧銀行のデータベースエンジニアで、主にデータベースの日常的な運用と保守、自動化の構築、DMPプラットフォームの運用と保守を担当しています。サイクリングや研究技術など、MySQL、Python、Oracle が得意です。

この記事の出典: 元の寄稿

* Aikesheng オープン ソース コミュニティによって作成されたオリジナル コンテンツは、許可なく使用することはできません。編集者に連絡し、転載のソースを示してください。


I.はじめに

データベースにおける一般的な並行性の問題としてのデッドロック。そのような質問:

1. トリガーの理由は、多くの場合、アプリケーションのロジックに関連しており、関連するトランザクションは 2 つ、3 つ、またはそれ以上になる場合があります。

2. 異なるデータベースのロック実装メカニズムはほぼ完全に異なり、実装ロジックは複雑であるため、依然として多くの種類のロックがあります。

3. データベースでデッドロックが発生した後、一部のトランザクションはすぐに終了し、デッドロック前の待機状態は後でわかりません。

つまり、デッドロックの問題は、ビジネス関連、複雑なメカニズム、およびさまざまなタイプの特性を持っているため、データベースでデッドロックの問題が発生した場合の分析はそれほど簡単ではありません。

この記事では、デッドロック問題の解決の難しさを踏まえ、MySQL データベースでの同時挿入によるデッドロックを例に、問題の特定、問題の再現、根本原因の分析、問題の解決の 4 つの手順を説明します。 , それは一連のデッドロックを提供することが期待されています. 読者や友人が参照するための科学的で効果的な解決策.

2.問題現象

システムの起動前のストレス テスト中に、アプリケーション ログにデッドロックの問題を引き起こす次のログ プロンプトが含​​まれていることがわかりました。

Deadlock found when trying to get lock; try restarting transaction

幸いなことに、ストレス テスト中に問題が発見され、オンラインになった後の本番環境への影響は回避されました。

次に、次の内容の show engine innodb status を実行します (感度低下後)。

------------------------
LATEST DETECTED DEADLOCK
------------------------
2023-03-24 19:07:50 140736694093568
*** (1) TRANSACTION:
TRANSACTION 56118, ACTIVE 6 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1192, 1 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 140736685700864, query id 57 localhost root update
insert into dl_tab(id,name) values(30,10)

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56118 lock mode S waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;  # 十进制: 10
 1: len 4; hex 8000001a; asc     ;;  # 十进制: 26


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56118 lock mode S waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;  # 十进制: 10
 1: len 4; hex 8000001a; asc     ;;  # 十进制: 26


*** (2) TRANSACTION:
TRANSACTION 56113, ACTIVE 12 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1192, 2 row lock(s), undo log entries 2
MySQL thread id 8, OS thread handle 140736952903424, query id 58 localhost root update
insert into dl_tab(id,name) values(40,8)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56113 lock_mode X locks rec but not gap
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;; # 十进制: 10
 1: len 4; hex 8000001a; asc     ;; # 十进制: 26


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56113 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;; # 十进制: 10
 1: len 4; hex 8000001a; asc     ;; # 十进制: 26

*** WE ROLL BACK TRANSACTION (1)
------------

1.デッドロック情報結合

上記の情報から、dl_tab が挿入操作を実行し、デッドロックが発生していることが判明しました。予備コーミングは次のとおりです。

バージョン: 8.0.27

分離レベル: Read-Commited

テーブル構造:

*************************** 1. row ***************************
       Table: dl_tab
Create Table: CREATE TABLE `dl_tab` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` int NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ua` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

上記の innodb ステータス出力は、データベースのバージョンによって異なることに注意してください。がある:

A. MySQL 8.0.18 以降では、出力には 2 つのトランザクションによって保持されているロックと、それらが待機しているロックが含まれます。これは、デッドロックの問題を分析するのに非常に役立ちます。8.0.18 より前では、トランザクション 1 を待機しているロック、トランザクション 2 によって保持されているロック、および待機中のロックのみが含まれていましたが、トランザクション 1 によって保持されていたロック情報は含まれていませんでした。

B. 上記の例には、特定のインデックス レコード値も含まれています ({10,26} など: 最初のフィールドはインデックス レコードの値で、2 番目のフィールドは対応する主キー レコードです)。インデックス レコード値がない場合、ヒープ番号のみが存在する可能性があります。この番号は、内部ロック メカニズムとして非常に重要ですが、特定のインデックス レコードに関連付けることはできません。さらに、他のいくつかの MySQL バージョンを見つけたところ、元のバージョン 5.7.21 以上および 8.0 以上にはこの機能があることがわかりましたが、Percona mysql 5.7.21 にはこの機能がありません。

C. トランザクション T1 が待機しているロックは、出力結果から LOCK S ですが、実際に獲得したロックは lock s next key lock であり、これについては後でソース コード解析結果で詳しく説明します。

Innodb ステータス ログの並べ替え:

事務 トランザクション T1 トランザクション T2
操作する dl_tab(id,name) 値 (40,8) に挿入 dl_tab(id,name) 値 (30,10) に挿入
関連オブジェクト testdb.dl_tab の一意のインデックス ua testdb.dl_tab の一意のインデックス ua
ロック保持 lock_mode X は rec をロックしますが、ギャップ
ヒープはロックしません 6
10,26
ロックモード S 待機
ヒープ番号 6
10 (16 進数で a)、26 (16 進数で 1a)
ロック待ち lock_mode X は rec 挿入意図待機
ヒープ前のギャップをロックします 6
10,26
ロック モード S 待機
ヒープ 6
10,26

上記の innodb status 出力から。ユニークインデックスuaでデッドロックが発生していることがわかります。これは実際、RC 分離レベル構成での一般的なデッドロック シナリオです。デッドロック プロセスをさらに整理します。

A. まず、トランザクション T1 が ua のレコード 10 のlock x,rec not not gapロック

B. トランザクション T2 は ua でレコード 10 を取得しようとしますlock s, next key lock。T1 がレコードの排他ロックを保持しているため、T1 によってブロックされます。

C. トランザクション T1 は ua でレコード 10 を取得しようとしますlock x, gap before rec,insert intentionが、ブロックされます

2. 質問する

上記の現象に加えて、出力結果から次のような情報を取得できません。

Q1: T1 が ua でレコード 10 のロックを保持しているのはなぜですか?

Q2: T1 がロックを保持しているのに、ロックを待機するのはなぜですか?

Q3: T2 は同じロックを保持して待機していますが、保持していますか、それとも待機していますか?

Q4: デッドロックはどのように発生しますか?

そのために、R&D の学生とコミュニケーションを取り、デッドロックが発生したビジネス シナリオを理解し、問題を再現しました。

3. 問題を再現する

研究開発の学生は、特定のビジネスのスケジュールされたタスクが同時に処理されたときにトリガーされ、対応する開発環境で問題がすぐに再現されることを発見しました。問題が再発した後、アプリケーション ログと innodb ステータス出力に対応するログが表示され、同じ問題であることを確認できます。

読者や友人は、さらに分析する方法について 1 分間考えることをお勧めします

1. 解いてみる

問題を先に解決するという原則に基づき、ユニークインデックス ua が同時発生した場合に生成されますが、ユニークインデックスを通常のインデックスに変更することはできますか? そうでない場合、同時実行数を減らす (または単一の同時実行数に直接変更する) ことはできますか? しかし、研究開発の学生はすぐに、uname の唯一のインデックスはコア フレームワークに依存しており、変更できないことを確認しました。この機能のリアルタイム要件は非常に高く、オンラインになった後の業務量は比較的大きいため、同時実行性を減らしたり減らしたりすることはできません。これは避けられないため、デッドロックの原因をさらに分析し、それに応じて解決策を確認するしかありません。

R&D が問題を再現した後も、アプリケーション ログと innodb ステータス出力以外の情報はありませんでした。また、R&D はテスト環境を参照して、データのバッチを作成し、それをシミュレートして再現します。つまり、デッドロックは再現できますが、元の質問 (Q1、Q2、Q3、Q4) には回答できません。

2. デッドロック発生過程の追跡

次に、Percona が記事 (リンク: MySQL デッドロックに対処する方法) を提供しているのを見つけました。これは、デッドロックの問題を分析するためのアイデアを大まかに提供しています: アプリケーションログと innodb ステータスによって提供されるデータに基づいて、events_statements_history、binlog と組み合わせます(できればステートメント形式)、スロー クエリ ログ (スロー ログ)、および分析用の一般ログ (一般ログ)。

記事によると、既存の関数 (events_statements_history/slow log/general log) を使用して、データベース接続がどの SQL ステートメントを実行したかを調べます。その後、スクリプトは次のように要約されます。

-- 将events_statements_history 中的启动时间转换为标准时间
create database IF NOT EXISTS perf_db;
use perf_db;
DELIMITER //
create function f_convert_timer_to_utc(pi_timer bigint) returns timestamp(6)
DETERMINISTIC
begin
    declare value_utc_time timestamp(6);
    select FROM_UNIXTIME( (unix_timestamp(sysdate()) - variable_value) + pi_timer/1000000000000 )  from performance_schema.global_status where variable_name = 'Uptime' into value_utc_time;
    return value_utc_time;
end;
//
DELIMITER ;

--innodb status 输出中,死锁信息中MySQL thread id,实际表示是PROCESSLIST ID。执行语句找到thread_id 与PROCESSLIST_ID的对应关系
select PROCESSLIST_ID,THREAD_ID,PROCESSLIST_INFO from performance_schema.threads where PROCESSLIST_ID in (8,10);

-- 找到上一步找到的线程ID找到运行过的SQL语句
select THREAD_ID,
 perf_db.f_convert_timer_to_utc(TIMER_START) run_start_time,
 perf_db.f_convert_timer_to_utc(TIMER_END) run_end_time,
 TIMER_WAIT/1000000000000 wait_time_s,
 'False' is_current,
 CURRENT_SCHEMA,
 SQL_TEXT
 from performance_schema.events_statements_history where thread_id=51
union 
select THREAD_ID,
 perf_db.f_convert_timer_to_utc(TIMER_START) run_start_time,
 perf_db.f_convert_timer_to_utc(TIMER_END) run_end_time,
 TIMER_WAIT/1000000000000 wait_time_s,
 'True' is_current,
 CURRENT_SCHEMA,
 SQL_TEXT
 from performance_schema.events_statements_current where thread_id=51
 and (THREAD_ID,EVENT_ID,END_EVENT_ID) not in (select THREAD_ID,EVENT_ID,END_EVENT_ID from performance_schema.events_statements_history )
order by run_start_time;

注: 上記のスクリプトの赤いテキストは、実際の状況に応じて置き換える必要があります。

スクリプトを整理した後、R&D の学生は再びデッドロックを再現しようとしました。クエリの結果は次のとおりです。

select PROCESSLIST_ID,THREAD_ID,PROCESSLIST_INFO from performance_schema.threads where PROCESSLIST_ID in (8,10);

+----------------+-----------+------------------+
| PROCESSLIST_ID | THREAD_ID | PROCESSLIST_INFO |
+----------------+-----------+------------------+
|              8 |        49 | NULL             |  
|             10 |        51 | NULL             | 
+----------------+-----------+------------------+

thread_id=49的sql运行情况:
|        49 | 2023-03-25 02:15:59.173352 | 2023-03-25 02:15:59.173612 |      0.0003 | False      | testdb         | begin                                     |
|        49 | 2023-03-25 02:16:08.349311 | 2023-03-25 02:16:08.350678 |      0.0014 | False      | testdb         | insert into dl_tab(id,name) values(26,10) |
|        49 | 2023-03-25 02:16:26.824176 | 2023-03-25 02:16:26.826121 |      0.0019 | False      | testdb         | insert into dl_tab(id,name) values(40,8)  |
+-----------+----------------------------+----------------------------+-------------+------------+----------------+-------------------------------------------+

thread_id=51 的sql运行情况:
|        51 | 2023-03-25 02:15:58.040749 | 2023-03-25 02:15:58.041057 |      0.0003 | False      | testdb         | begin                                     |
|        51 | 2023-03-25 02:16:17.408110 | 2023-03-25 02:16:26.828374 |      9.4203 | False      | testdb         | insert into dl_tab(id,name) values(30,10) |

コーミングの結果は次のとおりです。

トランザクション T1 – スレッド 49 トランザクション T2 – スレッド 51
始める; 始める;
dl_tab(id,name) 値 (26,10) に挿入します。
dl_tab(id,name) 値 (30,10) に挿入します。
dl_tab(id,name) 値 (40,8) に挿入します。

上記の集計結果によると、上記のSQL文を開発環境で手動で実行すると、再びデッドロックが発生し、innodbステータスのデッドロック情報はテスト環境と基本的に同じです。

これまでのところ、最初の質問 Q1 は次のように回答されています。

Q1: T1 が ua でレコード 10 のロックを保持しているのはなぜですか?

回答: トランザクションの前に次のステートメントが実行されたため、レコード (26,10) のロックが保持されます。insert into dl_tab(id,name) values(26,10);

3. デッドロックの追跡に関するその他の考慮事項

今回のデッドロックの発生から、innodb status デッドロック情報の出力にある 2 つのセッションがデッドロックの原因となっていました。ただし、3 つ、4 つ、またはそれ以上のトランザクションがデッドロックに関与している可能性があるため、次のような追加の問題がいくつかあります。

Q5: 3 つ以上のトランザクションがデッドロックに参加しているかどうかを追跡する方法を教えてください。

Q6: 実行された SQL ステートメントがデッドロックの最も直接的な原因である必要があります.ロックの本質は、レコード、ロックの種類、およびブロック関係にあります.それを確認する方法は?

Q7: デッドロックが発生すると、MySQL デッドロック検出メカニズムが自動的にデッドロックを検出し、ロールバックするトランザクションを選択します。トランザクションがロールバックされた後、デッドロックの最初のシーンが破棄されます。innodb status によって提供される最新のデッドロック情報 (特に、トランザクション 1 によって保持されるロック情報は 8.0.18 より前には含まれていませんでした) を除いて、利用可能な他の分析データはありません。

上記の 3 つの問題に基づいて、関連するパフォーマンス データを収集するために、次の補足ソリューションを要約します。

  • Q7 の場合: テスト環境でデッドロック検出を一時的に無効にしてから、もう一度再現します。
innodb_deadlock_detect = off
innodb_lock_wait_timeout = 10
innodb_rollback_on_timeout = on
  • Q5 および Q6 について: MySQL の既存のリアルタイム ロックおよびロック待機パフォーマンス データと組み合わせて、次のスクリプトを要約します。
-- 创建工作目录
cd <path-to-dir>
mkdir deadlock_data
cd deadlock_data

-- 创建死锁数据保存表
mysql -uroot -S /tmp/mysql.sock
create database IF NOT EXISTS perf_db;
use perf_db
CREATE TABLE `tab_deadlock_info` (
  `id` int primary key auto_incrment,
  `curr_dt` datetime(6) NOT NULL,
  `thread_id` bigint unsigned DEFAULT NULL,
  `conn_id` bigint unsigned DEFAULT NULL,
  `trx_id` bigint unsigned DEFAULT NULL,
  `object_name` varchar(64) DEFAULT NULL,
  `INDEX_NAME` varchar(64) DEFAULT NULL,
  `lock_type` varchar(32) NOT NULL,
  `lock_mode` varchar(32) NOT NULL,
  `lock_status` varchar(32) NOT NULL,
  `LOCK_DATA` varchar(8192) CHARACTER SET utf8mb4 DEFAULT NULL,
  `blk_trx_id` bigint unsigned DEFAULT NULL,
  `blk_thd_id` bigint unsigned DEFAULT NULL,
  index idx_curr_dt(curr_dt)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 查看当前存在的锁及锁堵塞信息
-- data_locks/data_lock_waits 自MySQL 8.0.1提供,之前版本查询information_schema.innodb_locks/ information_schema.innodb_lock_waits获取类似信息
vi save_lock_data.sql
insert into tab_deadlock_info(curr_dt,thread_id,conn_id,trx_id,object_name,index_name,
     lock_type,lock_mode,lock_status,lock_data,blk_trx_id,blk_thd_id)
select NOW(6) curr_dt,a.thread_id,b.PROCESSLIST_ID conn_id,
  ENGINE_TRANSACTION_ID trx_id, object_name,
  INDEX_NAME,lock_type,lock_mode,lock_status,LOCK_DATA,
  c.BLOCKING_ENGINE_TRANSACTION_ID blk_trx_id,
  c.BLOCKING_THREAD_ID blk_thd_id
from performance_schema.data_locks a left join performance_schema.threads b 
  on a.thread_id=b.thread_id
left join performance_schema.data_lock_waits c 
     on a.ENGINE_TRANSACTION_ID=c.REQUESTING_ENGINE_TRANSACTION_ID and a.thread_id=c.REQUESTING_THREAD_ID
where a.thread_id=b.thread_id order by thread_id,trx_id;

-- 查询脚本
mysql -uroot -S /tmp/mysql.sock perf_db -e "source save_lock_data.sql"

-- 定时查询脚本
vi run_save_lock.sh
while true
do
sleep 0.1 # 指定查询间隔时间,结合实际需求调整
mysql -uroot -S /tmp/mysql.sock perf_db -e "source save_lock_data.sql" 2>>run_save_lock.err
done

-- 执行查询
sh run_save_lock.sh

説明します:

A. 上記の innodb_deadlock_detect パラメータを構成してデッドロック検出を無効にすると、innodb ステータスは最後の LATEST DETECTED DEADLOCK 情報を出力し続けなくなります。

B. アプリケーション ログに表示されるアラームは、ロック タイムアウト アラームです: ロック待機タイムアウトを超過しました; トランザクションを再起動してみてください。これに基づいて、ロック タイムアウトがいつ発生したかを確認できます。

再表示されたら、次のように tab_deadlock_info を使用してロック データをクエリします。

同時に、ステップ 2) を使用して照会されたステートメント情報は次のとおりです。

上記のクエリ結果に基づいて、次の結果が得られます。

時間 トランザクション T1 トランザクション T2
2023-03-27 14:53:49 始める;
2023-03-27 14:53:50 始める;
2023-03-27 14:53:54 dl_tab(id,name) 値 (26,10) に挿入します。
2023-03-27 14:53:59 保持: ロック X、ua レコードの Rec_not_gap (10,26) insert into dl_tab(id,name) values(30,10);
wait: Lock S of ua record (10,26)
2023-03-27 14:54:04 insert into dl_tab(id,name) values(40,8);
hold: Lock X of ua record (10,26), Rec_not_gap
waiting: Lock X, gap, insert_intention of ua record (10,26)

デッドロックの発生過程を再度整理する。

4. 根本原因分析

以上の処理により、デッドロックの発生過程と獲得したロックとその属性情報を確認することができます。しかし、デッドロックが発生する理由を分析するには、MySQL のロック実装メカニズムを組み合わせる必要もあります。上記のデッドロック シナリオにより、一意のインデックスを含む挿入実装ロジックは、ソース コードと連動して解釈されます。

1. 単一列の一意のインデックスの挿入ロジック

下の図:

青い線は、最初の挿入で T1 によって実行されるロジックを表します。

紫色の線は、T2 の最初の挿入によって実行されるロジックを表します。

黒い線は、T1 の 2 回目の挿入によって実行されるロジックを表します。

一意のインデックス挿入レコードに関連するロック操作は、赤い短い矢印でマークされています。

赤い短い先のとがった頭を横切る縦線は、関数が実行されていることを意味し、それ以外の場合は実行されていないことを意味します。

2. 最終的なデッドロック プロセス

時間ディメンションに基づいて、分析のために上記の mysql ロック ロジックと組み合わせます。

A. T1 と T2 がトランザクションを開始し、T1 が (26,10) の挿入ステートメントを実行します。

B. T2 は、insert ステートメントを実行して (30,10) を挿入します。一意性の競合を確認し、LOCK_S | LOCK_ORDINARY [15 行目] を取得してみてください。次に、LOCK S が表示される理由は、LOCK_ORDINARY に対応する数値が 0 として表現され、それを使用した「AND」演算はそれ自体と等しいため、表示されないためです。

C. 次に、T2 が配置されている接続は、T1 の暗黙的なロックを明示的なロックに変換します [17 行目].この時点で、T1 は LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP、つまり、ロック X、Rec_not_gap を取得します。T2 が配置されているスレッドは T1 のロックを作成するため、ロックに対応する thread_id は T2 のスレッド ID ですが、trx_id は T1 のトランザクション ID です。

D. しかし、T1 の LOCK X|LOCK_REC_NOT_GAP は T2 の LOCK S|LOCK_ORDINARY [23 行目] と互換性がないため、T2 はブロックされます。

E. T2 がブロックされると、内部関数 add_to_waitq は処理時にロックの作成も記録し、属性 LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC を設定して、ロックが待機中であることを示します [24 行目]。次に、T2 は上位層関数に戻り、ロック リソースを待ちます [38 行目]

F. 続いて、T1 は (40,8) の挿入ステートメントを実行します。挿入される一意のインデックス値は 8 であるため (10 ではないことに注意してください)、主キーの競合はなく、オプティミスティックな挿入操作が直接実行されます [43 行目]。

G. 楽観的な挿入を実行する場合、他のトランザクションが挿入操作をブロックしていないかどうかを確認する必要があります。そのコアは、挿入されるレコードの次の値を取得することです [行 46-47] (ここでは正確に 10 です)。レコードのすべてのロックを取得し、必要なロックとの競合があるかどうかを判断します。追加 (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION) [ 51 行目]。

H. ステップ E で、T2 は LOCK_S | LOCK_ORDINARY | LOCK_WAIT | レコード 10 の LOC を保持します。

K_REC ロックは T1 の LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION と互換性がないため、T1 は T2 によってブロックされます [51 行目]

I.デッドロック形成。

実装の詳細を知る必要がある場合は、ソース コードでさらに確認できます。

これまでのところ、最初の質問 Q2、Q3、および Q4 は回答済みです。

Q2: T1 がロックを保持しているのに、ロックを待機するのはなぜですか?

回答: ロックを保持することに疑いの余地はありません. 同様の問題を分析する際には、暗黙的なロックを明示的なロックに変換するメカニズム (lock_rec_convert_impl_to_expl) に注意してください。ロックの待機は主に、T2 がブロックされた後にロック (LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC) が作成されるという事実によるものです。次に、T1 が楽観的な挿入を実行するとき、ロックの競合を判断するために、レコード上に存在するすべての > ロックをトラバースする必要があります。

Q3: T2 は同じロックを保持して待機していますが、保持していますか、それとも待機していますか?

回答: T2 がロックを保持し、次のキー ロックを待機していることは、innodb の状態からわかります。しかし、実際には lock の次のキー ロックを待ちます; ロックの競合のため、待機キューに参加するときにロックが保持されます LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC

Q4: デッドロックはどのように発生しますか?

回答: 上記のデッドロック プロセス分析の分析を参照してください。

5. 問題を解決する

以上のデッドロックの発生過程と原因を整理すると、以下のようにまとめられます。

理由 1: 主な研究開発がユニーク インデックスに過度に依存しており、挿入されたデータがユニーク インデックスの要件を満たしていないため、ユニーク コンフリクト チェックが必要です。

理由 2: バッチで挿入されたデータの順序が正しくありません。両方の条件が同時に存在するため、デッドロックが発生します。

理由 2 は、同時実行シナリオでの制御がより複雑です。理由 1 このシナリオは同時バッチ挿入ロジックであり、挿入の実行時に重複する unames の挿入を回避できます。その後、R&D の学生がロジックを最適化したところ、問題は発生しなくなりました。

デッドロックの問題については、ビジネス状況に基づいて、可能な限り Read Committed 分離レベルを選択し、Unique インデックスを適切に減らすことをお勧めします。デッドロックが発生した場合、読者や友人はこの失敗事例を参照し、パフォーマンス データを合理的に使用してデッドロックの問題を追跡し、ソース コードまたは既存のケースと組み合わせてデッドロックの根本原因と解決策を分析できます。

上記の情報は左右のやり取りのみで、作者のレベルは限定的ですので、不足等ありましたらコメント欄にてご連絡ください。

おすすめ

転載: blog.csdn.net/ActionTech/article/details/130323924