出典:パブリックアカウントyangyidba
I.はじめに
デッドロックは、実際には非常に興味深く、やりがいのある技術的な問題です。おそらく、すべてのDBAと一部の開発者は、作業の過程でデッドロックに遭遇するでしょう。デッドロックについては、デッドロックを理解したい友達を助けたいと思い、一連のケーススタディを書き続けていきます。
2つのケース分析
2.1ビジネスシナリオ
主なビジネスロジック:
最初に挿入データを実行し、挿入が成功した場合は送信します。挿入時に一意のキーの競合が報告された場合、更新が実行されます。3つの同時データ初期化アクションが同時に発生し、sess1挿入が正常に行われ、sess2とsess3の挿入で一意のキーの競合が発生し、挿入が失敗した場合、両方が更新を実行するため、デッドロックが発生します。
2.2環境への準備
MySQL5.6.24トランザクション分離レベルはRRです
create table ty (
id int not null primary key auto_increment ,
c1 int not null default 0,
c2 int not null default 0,
c3 int not null default 0,
unique key uc1(c1),
unique key uc2(c2)
) engine=innodb ;
insert into ty(c1,c2,c3) values(1,3,4),(6,6,10),(9,9,14);
2.3テストケース
デッドロックログの分析を容易にするために、3つのセッションに挿入されたc3の値はそれぞれ1 2 3であり、実際には本番環境では同じ値です。
sess1 |
sess2 |
sess3 |
|
ベギン; |
ベギン; |
ベギン; |
|
T1 |
ty(c1、c2、c3)に挿入values(4,4,1); |
||
T2 |
ty(c1、c2、c3)に挿入values(4,4,2); |
||
T3 |
ty(c1、c2、c3)に挿入values(4,4,3); |
||
T4 |
コミット |
||
T5 |
ty set c3 = 5を更新します。ここでc1 = 4; |
||
T6 |
ty set c3 = 5を更新します。ここでc1 = 4; エラー1213(40001):ロックを取得しようとしたときにデッドロックが見つかりました。トランザクションを再開してみてください |
2.4デッドロックログ
2018-03-28 10:04:52 0x7f75bf2d9700
*** (1) TRANSACTION:
TRANSACTION 1870, ACTIVE 76 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 399265, OS thread handle 12, query id 9 root updating
update ty set c3=5 where c1=4
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 4 n bits 72 index uc1 of table
`test`.`ty` trx id 1870 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 1871, ACTIVE 32 sec starting index read,
thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 399937, OS thread handle 16, query id 3 root updating
update ty set c3=5 where c1=4
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 28 page no 4 n bits 72 index uc1 of table
`test`.`ty` trx id 1871 lock mode S
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 4 n bits 72 index uc1 of table
`test`.`ty` trx id 1871 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (2)
実際、ログを見るだけでは、2つのトランザクションの更新が互いに競合していることがわかります。ビジネスロジックのシナリオがない場合、挿入操作がないため、デッドロックを分析するための効果的なアイデアを得るのは困難です。
2.5デッドロックログを分析する
T1 s1は挿入操作を実行し、一意性をチェックして正常に挿入し、c1 = 4レコード行の行ロックを保持します。
T2 s2挿入で一意のキーの競合が発生し、ロックロックSに適用次のキーロックログに、テーブルtest.ty trx id1870ロックモードSのインデックスuc1が表示されます。
T3はs2と同じで、s3挿入で一意のキーの競合が発生し、ロックロックSに適用します。次のキーのロックログには、テーブルtest.ty trx id1870ロックモードSのインデックスuc1が表示されます。
T4 sess1はコミット操作を実行します。このとき、sess2とsess3はロックSネクストキーロックを同時に取得します。
T5アプリケーションは、一意のキーの競合を受け取ります。sess2の更新操作は、c = 4の行ロックを適用する必要があります。これは、sess3が保持するLock S Next-keyLockと互換性がありません。sess3がLockS Next-keyLockを解放するのを待ちます。
T6はsess2に似ています。sess3の更新操作はc = 4の行ロックを適用する必要があります。これは、sess2が保持するLock S Next-keyLockと互換性がありません。sess2がLockS Next-keyLockを解放するのを待っています。周期的な待機が発生し、デッドロックが発生します。
2.6解決策
この場合の解決策と実際には、重複キーに挿入を使用して、以前は同じ7つのケースをデッドロックしました。ケース7は、この記事でデッドロックを引き起こしたビジネスロジックと非常によく似ています。なぜですか。同じグループの開発者によって書かれたからです。
3つの要約
デッドロックの根本的な原因は、トランザクションごとにロックを適用する順序が異なり、循環的な待機が発生することです。同時実行性の高いビジネスシナリオを設計する場合、開発者はこの点に焦点を当て、不合理なビジネスシナリオによって引き起こされるデッドロックを回避する必要があります。
さらに、インサートのロックメカニズムは実際には更新よりも複雑であり、ロックプロセスを明確にするためにより多くの実践的な練習が必要です。
QRコードをスキャンして、作成者のWeChatパブリックアカウントをフォローします
拡張読書
全文は終わりました。
MySQLをお楽しみください:)
TeacherYeの「MySQLCoreOptimization」クラスがMySQL8.0にアップグレードされました。コードをスキャンして、MySQL8.0の練習の旅を始めてください。