ケーススタディデータベースのデッドロック

免責事項:この記事はブロガーオリジナル記事ですが、許可ブロガーなく再生してはなりません。https://blog.csdn.net/wufaliang003/article/details/91410623

序文

非常に興味深く、非常にチャレンジングな技術的な問題であるデッドロックは、実際には、おそらく開発及びDBA学生の各部分は、自分の仕事の過程で会う予定です。デッドロックの話は、私は友人がデッドロックを知りたい助けることを望んで、分析のシリーズを書き続けます

二つのケーススタディ

2.1ビジネスシーン

ユーザ入力品は、削除してから挿入した場合、アプリケーションは、同じレコードかどうかを事前に確認します。直接挿入されていない場合。

2.2環境の指示

MySQLの5.7.22トランザクション分離レベルRCモード。

  1. create table t(id int not null auto_increment primary key ,

  2. a int not null default 0,

  3. b int not null default 0,

  4. c int not null default 0,

  5. unique key uk_ab(a,b)) engine=innodb;

  6. insert into t(a,b,c) values(1,1,1),(3,3,2),(6,6,3),(9,9,5);

2.3背景

知識ポイント

挿入またはレコードを更新する際にINSERT操作は、重複したキーを確認したり、重複したキーを削除対象としてマークされた属性は、次のキーロックLOCK_Sロック追加されます、通常のINSERT / UPDATEのために、(記事の場合)。DUPLICATE Xロックが適用されるONこのまたは類似のために... SQL INSERT INTO REPLACE。そして、異なるインデックスタイプのために異なっています:

コードの場所row0ins.cc:2013

  1. if (flags & BTR_NO_LOCKING_FLAG) {

  2. /* Set no locks when applying log

  3. in online table rebuild. */

  4. } else if (allow_duplicates) {

  5.  

  6. /* If the SQL-query will update or replace

  7. duplicate key we will take X-lock for

  8. duplicates ( REPLACE, LOAD DATAFILE REPLACE,

  9. INSERT ON DUPLICATE KEY UPDATE). */

  10.  

  11. err = row_ins_set_exclusive_rec_lock(

  12. lock_type, block, rec, index, offsets, thr);

  13. } else {

  14.  

  15. err = row_ins_set_shared_rec_lock(

  16. lock_type, block, rec, index, offsets, thr);

  17. }

知識ポイント2

レコードがデータページに挿入されたとき、関数呼び出しは常に(索引付けデータの挿入を除く)lock_rec_insert_check_and_lockロックチェックになり、ロックが場合、次のレコード現在のオブジェクトの挿入位置があるか否かをチェックします物理的に連続した記録を参照して、しかし論理的な順序で次のレコードに基づいていません。ロックオブジェクトは、次のレコードに存在しない場合は、次のレコードが現在のトランザクションのIDのセカンダリインデックスにセカンダリインデックスページ上の最大トランザクションIDを更新する場合は、直接リターンの成功。

次のレコードのロックオブジェクトがある場合、ロックオブジェクトは、GAPがロックされているかどうかを決定する必要があります。GAPはロックされ、かつ意図を決定し、GAPのロックの競合を挿入された場合、現在の操作を待つ必要がある、プラスロック式LOCKX | LOCKGAP | LOCKINSERTINTENTION、および待機状態に入ります。コードの場所lock0lock.cc:5965

  1. *inherit = TRUE;

  2. /* If another transaction has an explicit lock request which locks

  3. the gap, waiting or granted, on the successor, the insert has to wait.

  4.  

  5. An exception is the case where the lock by the another transaction

  6. is a gap type lock which it placed to wait for its turn to insert. We

  7. do not consider that kind of a lock conflicting with our insert. This

  8. eliminates an unnecessary deadlock which resulted when 2 transactions

  9. had to wait for their insert. Both had waiting gap type lock requests

  10. on the successor, which produced an unnecessary deadlock. */

  11.  

  12. const ulint type_mode = LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION;

  13. const lock_t* wait_for = lock_rec_other_has_conflicting(

  14. type_mode, block, heap_no, trx);

  15. if (wait_for != NULL) {

  16. RecLock rec_lock(thr, index, block, heap_no, type_mode);

  17. trx_mutex_enter(trx);

  18. err = rec_lock.add_to_waitq(wait_for);

  19. trx_mutex_exit(trx);

  20. } else {

  21. err = DB_SUCCESS;

  22. }

私は、次の試験によって確認しました。表2.2データ構造とテスト・データ構造。

テストケース

  1. sess1

  2. mysql > delete from t where a=3 and b=3 ;

  3. Query OK, 1 row affected (0.00 sec)

  4. sess2

  5. mysql >update t set c=6 where a=6 and b=6 and c=3;

  6. sess1

  7. mysql >insert into t(a,b,c) values(3,3,5); --产生锁等待

挿入SロックX行ロックを保持削除sess2ブロックされている(3,3,5-)アプリケーションロック、

ショーエンジンInnoDBのステータスは、完全なロックSがロックするものでは表示されません。我々はテストし続けます。

テストケースII

  1. T1 sess1

  2. mysql > delete from t where a=3 and b=3 ;

  3. mysql > insert into t(a,b,c) values(3,3,5);

  4.  

  5. T2 sess2

  6. mysql > insert into t(a,b,c) values(3,2,6);

  7.  

  8. T3 sess3

  9. mysql > insert into t(a,b,c) values(3,4,5);

前記sess2 sess3待っている二つの部分のXが明らかにLOCK S次のキーロック閉塞sess1保持REC挿入意図待機前にギャップをロック及び(1,3)であるアプリケーションlock_modeに、(3,6)

明らかsess2ホールドロックXのレコードロックのレコード(6,6)ではないギャップのテストケースは、インサート(3,3)アプリケーションロックS次のキーロックをブロックします。

2.4テストケース

2.5デッドロックのログ

  1. 2019-04-27 23:26:16 0x7f26cc77b700

  2. *** (1) TRANSACTION:

  3. TRANSACTION 2489, ACTIVE 43 sec inserting

  4. mysql tables in use 1, locked 1

  5. LOCK WAIT 5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2

  6. MySQL thread id 121125, OS thread handle 139804595451648, query id 526 localhost msandbox update

  7. insert into t(a,b,c) values(3,3,3),(3,1,2)

  8. *** (1) WAITING FOR THIS LOCK TO BE GRANTED:

  9. RECORD LOCKS space id 26 page no 4 n bits 80 index uk_ab of table `test`.`t` trx id 2489 lock mode S waiting

  10. *** (2) TRANSACTION:

  11. TRANSACTION 2490, ACTIVE 36 sec inserting

  12. mysql tables in use 1, locked 1

  13. 6 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 3

  14. MySQL thread id 121123, OS thread handle 139804615882496, query id 528 localhost msandbox update

  15. insert into t(a,b,c) values(6,6,6),(6,5,4)

  16. *** (2) HOLDS THE LOCK(S):

  17. RECORD LOCKS space id 26 page no 4 n bits 80 index uk_ab of table `test`.`t` trx id 2490 lock_mode X locks rec but not gap

  18. *** (2) WAITING FOR THIS LOCK TO BE GRANTED:

  19. RECORD LOCKS space id 26 page no 4 n bits 80 index uk_ab of table `test`.`t` trx id 2490 lock_mode X locks gap before rec insert intention waiting

  20. *** WE ROLL BACK TRANSACTION (1)

デッドロックのログの分析2.6

T1 = 3およびコミットされていないB = 3、二次インデックスを保持削除(3,3)ライン・ロック・レコードが失効としてマークされます。

T2は、= 6、B = 6コミットされていないを削除し、二次インデックス(6,6)ライン・ロック・レコードが失効としてマークされて保持されます。

T3挿入(3,3,3)は、チェックがプラスLOCK_Sネクストキーロックを適用し、削除(3,3)のためにマークされています。しかし、次のレコード保持者ロックXのレコードロックにチェックインチ だから、待ってください。

T4 insert (6,5,4) 写入(3,6)的区间,申请lock_mode X locks gap before rec insert intention waiting,但是需要等待T3会话LOCK_S next-key lock。于是相互等待,发生死锁。

2.7 解决方法

本质上是并发操作相邻记录导致死锁。和开发沟通,将业务逻辑做修改,如果发现录入的商品记录数和存在的记录数一样就做更新,不存在的则直接写入。降低直接操作相邻记录的可能性。

三 小结

以上分析是基于自己半路出家的阅读代码能力的出来的,不一定完全正确。如果大家有其他意见,请拍砖。

关注微信公众号和今日头条,精彩文章持续更新中。。。。。

 

 

おすすめ

転載: blog.csdn.net/wufaliang003/article/details/91410623