1. 程式人生 > 其它 >一個普通的死鎖案例

一個普通的死鎖案例

技術標籤:後端mysqlsql

目錄

1、死鎖日誌

2、重現與分析


1、死鎖日誌

------------------------
LATEST DETECTED DEADLOCK
------------------------
200526 17:49:17
*** (1) TRANSACTION:
TRANSACTION 7892ECEC4, ACTIVE 50 sec inserting                                  ## 事務ID=7892ECEC4,活躍了50s  
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1   ## 4個鎖,2個行鎖,1個undo log   
MySQL thread id 41920347, OS thread handle 0x7f8fe5598700, query id 26349859046   *.*.*.* databaseA_rw update                                                     
                                                                                ## 該事務的執行緒ID=41920347 
insert into  
        tableA
    ( 
    id, template_id, template_type, modify_time, create_time
   ) 
      values(null, '8eaf3990b3d4493198c0c13150965741', 2, now(), now())         ## 這是當前事務執行的SQL
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:                                    ## 上面SQL等待的鎖資訊  
RECORD LOCKS space id 1326 page no 4 n bits 256 index `UNIQ_IDX_TEMPLATE` of table `databaseA`.`tableA` trx id 7892ECEC4 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 168 PHYSICAL RECORD: n_fields 2; compact format; info bits 0   
                                                                                ## 申請的插入意向鎖等待在唯一索引的page num=4 256 bits持有的X GAP鎖(lock_mode X locks gap before rec),需等待T2中的GAP鎖釋放。

 0: len 30; hex 393064383166343162336330343833343930353432383962313863663932; asc 90d81f41b3c048349054289b18cf92; (total 32 bytes);
 1: len 4; hex 000000f5; asc ;;

*** (2) TRANSACTION:
TRANSACTION 7892F00C6, ACTIVE 35 sec inserting, thread declared inside InnoDB 500
mysql tables in use 1, locked 1                                                 ## 事務ID=7892F00C6,活躍了35s  
4 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1             ## 4個鎖,2個行鎖,1個undo log 
MySQL thread id 41920345, OS thread handle 0x7f8ff7041700, query id 26349859047 *.*.*.* database_rw update
insert into  
        tableA
    ( 
    id, template_id, template_type, modify_time, create_time
   ) 
      values(null, '8eaf3990b3d4493198c0c13150965741', 2, now(), now())
*** (2) HOLDS THE LOCK(S):                                                      ## 該事務持有的X GAP鎖
RECORD LOCKS space id 1326 page no 4 n bits 256 index `UNIQ_IDX_TEMPLATE` of table `databaseA`.`tableA` trx id 7892F00C6 lock_mode X locks gap before rec
Record lock, heap no 168 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 30; hex 393064383166343162336330343833343930353432383962313863663932; asc 90d81f41b3c048349054289b18cf92; (total 32 bytes);
 1: len 4; hex 000000f5; asc ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:                                    ## 申請的插入意向鎖等待在唯一索引的page num=4 256 bits持有的X GAP鎖(lock_mode X locks gap before rec),需等待T1中的GAP鎖釋放。
RECORD LOCKS space id 1326 page no 4 n bits 256 index `UNIQ_IDX_TEMPLATE` of table `databaseA`.`tableA` trx id 7892F00C6 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 168 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 30; hex 393064383166343162336330343833343930353432383962313863663932; asc 90d81f41b3c048349054289b18cf92; (total 32 bytes);
 1: len 4; hex 000000f5; asc ;;

*** WE ROLL BACK TRANSACTION (2)                                                ##   這裡選擇回滾事務2

4lock struct(s) 表示trx->trx_locks鎖鏈表的長度為4,每個連結串列節點代表該事務持有的一個鎖結構,包括表鎖,記錄鎖以及auto_inc鎖等。1 row lock(s)表示當前事務持有的行記錄鎖/gap 鎖的個數。

簡要的死鎖分析參考死鎖日誌中##。雖然死鎖後,有一個事務會回滾,但會導致業務的等待。這段死鎖對應的程式碼如下:

2、重現與分析

重現(儲存引擎:innodb, 隔離級別:RR)

T1T2
select * from tableA where templateId='111' for update(gap鎖)
select * from tableA where templateId='111' for update (gap鎖)
insert into tableA(id, template_id, template_type, modify_time, create_time)values(null, '111', 2, now(), now())
insert into tableA(id, template_id, template_type, modify_time, create_time)values(null, '111', 2, now(), now())

分析

當兩個事務同時通過select * from update,並且未命中任何記錄的情況下,得到了相同的gap鎖。此時再進入併發插入,當T1申請獲取插入意向鎖(insert intention lock),由於T2存在gap鎖會阻塞插入意向鎖,故T1會進入等待狀態。當T2執行insert時,同樣的T2獲取插入意向鎖由於T1存在的gap鎖進入等待狀態(insert intention lock相互不會阻塞),從而導致死鎖。

這裡再強調一點,gap鎖與gap鎖是相容的,插入意向鎖相互不會阻塞,插入意向鎖也不會阻塞gap lock,但gap lock會阻塞插入意向鎖,感覺gap lock存在的價值就是為了阻塞插入意向鎖。

最後,也是最重要的,寫這篇文章的目的,是分享一個連結:https://github.com/aneasystone/mysql-deadlocks