show c..."/>
溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MYSQL INNODB replace into 死鎖 及 next key lock 淺析

發(fā)布時間:2020-08-07 05:34:12 來源:ITPUB博客 閱讀:260 作者:gaopengtttt 欄目:MySQL數(shù)據(jù)庫
原創(chuàng):全文帶入了大量自我認知和理解,可能錯誤,因為水平有限,但是代表我努力分析過。



一、問題提出
問題是由姜大師提出的、問題如下:
表:
mysql> show create table c \G
*************************** 1. row ***************************
       Table: c
Create Table: CREATE TABLE `c` (
  `a` int(11) NOT NULL AUTO_INCREMENT,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`a`),
  UNIQUE KEY `b` (`b`)
) ENGINE=InnoDB 
1 row in set (0.01 sec)
開啟兩個會話不斷的執(zhí)行
replace into c values(NULL,1);
會觸發(fā)死鎖。問死鎖觸發(fā)的原因。

我使用的環(huán)境:
MYSQL 5.7.14 debug版本、隔離級別RR、自動提交,很顯然這里的c表中的可以select出來的記錄始終是1條
只是a列不斷的增大,但是這里實際存儲空間確不止1條,因為從heap no來看二級索引中,heap no 已經(jīng)到了
7,也就是有至少7(7-1)條記錄,只是其他記錄標記為del并且被purge線程放到了page free_list中。

二、準備工作和使用方法
1、稍微修改了源碼關(guān)于鎖的打印部分,我們知道每個事物下顯示鎖內(nèi)存結(jié)構(gòu)lock 
   struct會連接成一個鏈表,只要按照順序打印出內(nèi)存lock struct就打印出了
   所有關(guān)于這個事物顯示鎖全部信息和加鎖順序如下:

點擊(此處)折疊或打開

  1. ---TRANSACTION 184771, ACTIVE 45 sec
  2. 4 lock struct(s), heap size 1160, 3 row lock(s)
  3. MySQL thread id 2, OS thread handle 140737154311936, query id 642 localhost root cleaning up
  4. ---lock strcut(1):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  5. TABLE LOCK table `test`.`c4` trx id 184771 lock mode IX
  6. ---lock strcut(2):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  7. RECORD LOCKS space id 413 page no 4 n bits 72 index id2 of table `test`.`c4` trx id 184771 lock_mode X(LOCK_X)
  8. Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
  9.  0: len 4; hex 80000014; asc ;;
  10.  1: len 4; hex 80000014; asc ;;
  11. ---lock strcut(3):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  12. RECORD LOCKS space id 413 page no 3 n bits 72 index PRIMARY of table `test`.`c4` trx id 184771 lock_mode X(LOCK_X) locks rec but not gap(LOCK_REC_NOT_GAP)
  13. Record lock, heap no 4 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
  14.  0: len 4; hex 80000014; asc ;;
  15.  1: len 6; hex 00000002d1bd; asc ;;
  16.  2: len 7; hex a600000e230110; asc # ;;
  17.  3: len 4; hex 80000014; asc ;;
  18. ---lock strcut(4):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  19. RECORD LOCKS space id 413 page no 4 n bits 72 index id2 of table `test`.`c4` trx id 184771 lock_mode X(LOCK_X) locks gap before rec(LOCK_GAP)
  20. Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
  21.  0: len 4; hex 8000001e; asc ;;
  22.  1: len 4; hex 8000001e; asc ;;


正常的版本只有

點擊(此處)折疊或打開

  1. ---TRANSACTION 184771, ACTIVE 45 sec
  2. 4 lock struct(s), heap size 1160, 3 row lock(s)
  3. MySQL thread id 2, OS thread handle 140737154311936, query id 642 localhost root cleaning up
部分后面的都是我加上的,其實修改很簡單,innodb其實自己寫好了只是沒有開啟,我開啟后加上了序號來表示順序。
上面是一個 select * from c where  id2= 20 for update; b列為輔助索引的所有4 lock struct(s),可以看到有了這些信息分析
不那么難了。
這里稍微分析一下
表結(jié)構(gòu)為:
mysql> show create table c4;
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                  |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
| c4    | CREATE TABLE `c4` (
  `id1` int(11) NOT NULL,
  `id2` int(11) DEFAULT NULL,
  PRIMARY KEY (`id1`),
  KEY `id2` (`id2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
數(shù)據(jù)為:
mysql> select * from c4;
+-----+------+
| id1 | id2  |
+-----+------+
|   1 |    1 |
|  10 |   10 |
|  20 |   20 |
|  30 |   30 |
+-----+------+
4 rows in set (0.00 sec)
語句為:
 select * from c where  id2= 20 for update;
RR模式
從鎖結(jié)構(gòu)鏈表來看,這個語句在輔助索引分別鎖定了
id2:20 id1:20 LOCK_X|LOCK_ORDINARY 也就是NEXT KEY LOCK
同時鎖定了
id2:30 id1:30 LOCK_X|LOCK_GAP也就是gap lock不包含這一列
那么畫個圖容易理解黃色部分為鎖定部分:
MYSQL INNODB replace into 死鎖 及 next key lock 淺析
是不是一目了然?如果是rc那么鎖定的只有記錄了兩個黃色箭頭
表示gap沒有了就不在畫圖了

2、在死鎖檢測回滾前調(diào)用這個打印函數(shù)打印到err日志文件中,打印出全部的事物的顯示內(nèi)存lock struct如下,這里就
不給出了,后面會有replace觸發(fā)死鎖千事物鎖結(jié)構(gòu)的一個輸出


3、使用MYSQL TRACE SQL語句得到大部分的函數(shù)調(diào)用來分析replace的過程

修改出現(xiàn)的問題:修改源碼打印出所有l(wèi)ock struct 在線上顯然是不能用的。因為打印出來后show engine innodb status 會非常
長,甚至引發(fā)其他問題,但是測試是可以,其次修改了打印死鎖事物鎖鏈表到日志后,每次只要遇到死鎖信息可以打印
到日志,但是每次MYSQLD都會掛掉,但是不影響分析了。

三、預備知識(自我理解)
1、
Precise modes:
#define LOCK_ORDINARY 0 /*!< this flag denotes an ordinary
next-key lock in contrast to LOCK_GAP
or LOCK_REC_NOT_GAP */
默認是LOCK_ORDINARY及普通的next_key_lock,鎖住行及以前的間隙
#define LOCK_GAP 512 /*!< when this bit is set, it means that the
lock holds only on the gap before the record;
for instance, an x-lock on the gap does not
give permission to modify the record on which
the bit is set; locks of this type are created
when records are removed from the index chain
of records */
間隙鎖,鎖住行以前的間隙,不鎖住本行
#define LOCK_REC_NOT_GAP 1024 /*!< this bit means that the lock is only on
the index record and does NOT block inserts
to the gap before the index record; this is
used in the case when we retrieve a record
with a unique key, and is also used in
locking plain SELECTs (not part of UPDATE
or DELETE) when the user has set the READ
COMMITTED isolation level */
行鎖,鎖住行而不鎖住任何間隙
#define LOCK_INSERT_INTENTION 2048 /*!< this bit is set when we place a waiting
gap type record lock request in order to let
an insert of an index record to wait until
there are no conflicting locks by other
transactions on the gap; note that this flag
remains set when the waiting lock is granted,
or if the lock is inherited record */
插入意向鎖,如果插入的記錄在某個已經(jīng)鎖定的間隙內(nèi)為這個鎖
2、參數(shù)innodb_autoinc_lock_mode的值為1,也許不能保證replace into的順序。
3、infimum和supremum
   一個page中包含這兩個偽列,頁中所有的行未刪除(刪除未purge)的行都連接到這兩個虛列之間,其中
   supremum偽列的鎖始終為next_key_lock。
4、heap no
   此行在page中的heap no heap no存儲在fixed_extrasize 中,heap no 為物理存儲填充的序號,頁的空閑空間掛載在page free鏈表中(頭插法)可以重用,
   但是重用此heap no不變,如果一直是insert 則heap no 不斷增加,并非按照KEY大小排序的邏輯鏈表順序,而是物理填充順序 
5、n bits
   和這個page相關(guān)的鎖位圖的大小如果我的表有9條數(shù)據(jù) 還包含2個infimum和supremum虛擬列 及 64+11 bits,及75bits但是必須被8整除為一個字節(jié)就是
   80 bits
6、隱含鎖(Implicit lock)和顯示鎖(explict)
鎖有隱含和顯示之分。隱含鎖通常發(fā)生在 insert 的時候?qū)luster index和second index 都加隱含鎖,如果是UPDATE(DELETE)對cluster index加顯示鎖 輔助
索引加隱含鎖。目的在于減少鎖結(jié)構(gòu)的內(nèi)存開銷,如果有事務需要和這個隱含鎖而不兼容,這個事務需要幫助 insert或者update(delete)事物將隱含
鎖變?yōu)轱@示鎖,然后給自己加鎖,通常insert主鍵檢查會給自己加上S鎖,REPLACE、delete、update通常會給自己加上X鎖。

四、replace過程分析
通過replace的trace找到了這些步驟的大概調(diào)用:
首先我們假設(shè)
TRX1:replace 不提交
TRX2:replace 堵塞
TRX1:replace 提交
TRX2:replace 繼續(xù)執(zhí)行直到完成
這樣做的目的在于通過trace找到TRX2在哪里等待,確實如我所愿我找到了。

1、檢查是否沖突,插入主鍵 

點擊(此處)折疊或打開

  1.     569 T@4: | | | | | | | | >row_ins
  2.     570 T@4: | | | | | | | | | row_ins: table: test/c
  3.     571 T@4: | | | | | | | | | >row_ins_index_entry_step
  4.     572 T@4: | | | | | | | | | | >row_ins_clust_index_entry
  5.     573 T@4: | | | | | | | | | | | >row_ins_clust_index_entry_low
  6.     574 T@4: | | | | | | | | | | | | >btr_cur_search_to_nth_level
  7.     575 T@4: | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  8.     576 T@4: | | | | | | | | | | | | ib_cur: insert PRIMARY (366) by 183808: TUPLE (info_bits=0, 4 fields): {[4] %(0x00000005),[6] (0x000000020E00),[7] (0x 0A000001010100),[4] (0x00000001)}
  9.     577 T@4: | | | | | | | | | | | <row_ins_clust_index_entry_low 2649
  10.     578 T@4: | | | | | | | | | | <row_ins_clust_index_entry 3313
  11.     579 T@4: | | | | | | | | | <row_ins_index_entry_step 3589
2、檢查是否沖突,插入輔助索引,這里實際上就是會話2被堵塞的地方,如下解釋
(如果沖突回滾先前插入的主鍵內(nèi)容)

點擊(此處)折疊或打開

  1.     580 T@4: | | | | | | | | | >row_ins_index_entry_step 3589
  2.     581 T@4: | | | | | | | | | | >row_ins_sec_index_entry_low
  3.     582 T@4: | | | | | | | | | | | >btr_cur_search_to_nth_level
  4.     583 T@4: | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  5.     584 T@4: | | | | | | | | | | | >row_ins_scan_sec_index_for_duplicate
  6.     585 T@4: | | | | | | | | | | | | >btr_cur_search_to_nth_level
  7.     586 T@4: | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  8.     587 T@4: | | | | | | | | | | | | >btr_cur_search_to_nth_level
  9.     588 T@4: | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  10.     589 T@4: | | | | | | | | | | | | >row_vers_impl_x_locked_low
  11.     590 T@4: | | | | | | | | | | | | | info: Implicit lock is held by trx:183803
  12.     591 T@4: | | | | | | | | | | | | <row_vers_impl_x_locked_low 329
  13.     592 T@4: | | | | | | | | | | | | >thd_report_row_lock_wait
  14.     593 T@4: | | | | | | | | | | | | <thd_report_row_lock_wait 4246
  15.     594 T@4: | | | | | | | | | | | <row_ins_scan_sec_index_for_duplicate 2148
  16.     595 T@4: | | | | | | | | | | <row_ins_sec_index_entry_low 3043
  17.     596 T@4: | | | | | | | | | <row_ins_index_entry_step 3589
  18.     597 T@4: | | | | | | | | <row_ins 3758 
  19.     598 //wait here
  20.     這里我做trace的時候事物的trace停止在了這里我特意加上了598//wait here從下面的輸出
  21.     我們也能肯定確實這里觸發(fā)了鎖等待 
  22.     >row_vers_impl_x_locked_low
  23.     | info: Implicit lock is held by trx:183803
  24.     <row_vers_impl_x_locked_low 329
  25.     >thd_report_row_lock_wait
  26.     <thd_report_row_lock_wait 4246
  27.     等待獲得鎖過后重新檢查:
  28.     599 T@4: | | | | | | | | >row_ins
  29.     600 T@4: | | | | | | | | | row_ins: table: test/c
  30.     601 T@4: | | | | | | | | | >row_ins_index_entry_step
  31.     602 T@4: | | | | | | | | | | >row_ins_sec_index_entry_low
  32.     603 T@4: | | | | | | | | | | | >btr_cur_search_to_nth_level
  33.     604 T@4: | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  34.     605 T@4: | | | | | | | | | | | >row_ins_scan_sec_index_for_duplicate
  35.     606 T@4: | | | | | | | | | | | | >btr_cur_search_to_nth_level
  36.     607 T@4: | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  37.     608 T@4: | | | | | | | | | | | <row_ins_scan_sec_index_for_duplicate 2148
  38.     609 T@4: | | | | | | | | | | <row_ins_sec_index_entry_low 3043
  39.     610 T@4: | | | | | | | | | <row_ins_index_entry_step 3589
  40.     611 T@4: | | | | | | | | <row_ins 3810
我們可以隱隱約約看到row_ins_sec_index_entry_low和row_ins_clust_index_entry_low回檢查是否有重復的行
    分別代表是二級索引和聚集索引的相關(guān)檢查,因為就這個案例主鍵不可能出現(xiàn)重復值,而二級索引這個例子中肯定是
    重復的,索引row_ins_sec_index_entry_low觸發(fā)了等待,其實我們知道這里的鎖方式如下列子:
    
---lock strcut(2):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK chain! for this Trx
RECORD LOCKS space id 406 page no 4 n bits 72 index b of table `test`.`c` trx id 177891 lock_mode X(LOCK_X) waiting(LOCK_WAIT)
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 80000001; asc     ;;
 1: len 4; hex 80000006; asc     ;;
 
LOCK_X|LOCK_ORDINARY|LOCK_WAIT:需要X的next_key lock處于等待狀態(tài)他需要鎖定(infimum,{1,6}]這個區(qū)間。
這也是死鎖發(fā)生的關(guān)鍵一個環(huán)節(jié)。

3、這里涉及到了回滾操作,從下面的trace輸出我們也能看到確實做了回滾
   實際上事物2會堵塞在這里,因為我做trace的時候他一直停在
   這里不動了。為此我還加上598行說明在這里wait了

點擊(此處)折疊或打開

  1.     612 T@4: | | | | | | | | ib_que: Execute 9 (QUERY THREAD) at 0x7fffe804b7d8
  2.     613 T@4: | | | | | | | | ib_que: Execute 12 (ROLLBACK) at 0x7fffe804b6b0
  3.     614 T@4: | | | | | | | | ib_que: Execute 12 (ROLLBACK) at 0x7fffe804b6b0
  4.     615 T@4: | | | | | | | | ib_que: Execute 9 (QUERY THREAD) at 0x7fffe804b7d8
  5.     616 T@4: | | | | | | | | ib_que: Execute 9 (QUERY THREAD) at 0x7fffe800eec8
  6.     617 T@4: | | | | | | | | ib_que: Execute 10 (UNDO ROW) at 0x7fffe801b090
  7.     618 T@4: | | | | | | | | >btr_cur_search_to_nth_level
  8.     619 T@4: | | | | | | | | <btr_cur_search_to_nth_level 2005
  9.     620 T@4: | | | | | | | | >btr_cur_search_to_nth_level
  10.     621 T@4: | | | | | | | | <btr_cur_search_to_nth_level 2005
  11.     622 T@4: | | | | | | | | ib_que: Execute 10 (UNDO ROW) at 0x7fffe801b090
  12.     623 T@4: | | | | | | | | ib_que: Execute 9 (QUERY THREAD) at 0x7fffe800eec8
4、這個重復key會傳遞給SERVER層次,并且貌似重新初始化了事物(只是從trace猜測)

點擊(此處)折疊或打開

  1. 639 T@4: | | | | | | >handler::get_dup_key
  2.     640 T@4: | | | | | | | >info
  3.     641 T@4: | | | | | | | | >ha_innobase::update_thd
  4.     642 T@4: | | | | | | | | | ha_innobase::update_thd: user_thd: 0x7fffe8000b90 -> 0x7fffe8000b90
  5.     643 T@4: | | | | | | | | | >innobase_trx_init
  6.     644 T@4: | | | | | | | | | <innobase_trx_init 2765
  7.     645 T@4: | | | | | | | | <ha_innobase::update_thd 3073
  8.     646 T@4: | | | | | | | <info 14717
  9.     647 T@4: | | | | | | <handler::get_dup_key 4550
  10.     648 T@4: | | | | | | >column_bitmaps_signal
  11.     649 T@4: | | | | | | | info: read_set: 0x7fffc8941da0 write_set: 0x7fffc8941da0
  12.     650 T@4: | | | | | | <column_bitmaps_signal 3846
  13.     651 T@4: | | | | | | >innobase_trx_init
  14.     652 T@4: | | | | | | <innobase_trx_init 2765
  15.     653 T@4: | | | | | | >index_init
  16.     654 T@4: | | | | | | <index_init 8864
5、接下就是真正刪除插入主鍵

點擊(此處)折疊或打開

  1.     689 T@4: | | | | | | | | >row_update_for_mysql_using_upd_graph
  2.     690 T@4: | | | | | | | | | >row_upd_step
  3.     691 T@4: | | | | | | | | | | >row_upd
  4.     692 T@4: | | | | | | | | | | | row_upd: table: test/c
  5.     693 T@4: | | | | | | | | | | | row_upd: info bits in update vector: 0x0
  6.     694 T@4: | | | | | | | | | | | row_upd: foreign_id: NULL
  7.     695 T@4: | | | | | | | | | | | ib_cur: delete-mark clust test/(366) by 183808: COMPACT RECORD(info_bits=32, 4 fields): {[4] $(0x00000004),[6] (0x000000020D 0B),[7] (0x00000001090100),[4] (0x00000001)}
  8.     696 T@4: | | | | | | | | | | | >row_ins_clust_index_entry
  9.     697 T@4: | | | | | | | | | | | | >row_ins_clust_index_entry_low
  10.     698 T@4: | | | | | | | | | | | | | >btr_cur_search_to_nth_level
  11.     699 T@4: | | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  12.     700 T@4: | | | | | | | | | | | | | ib_cur: insert PRIMARY (366) by 183808: TUPLE (info_bits=0, 4 fields): {[4] %(0x00000005),[6](0x000000020E00),[7] ( 0x00000001090100),[4] (0x00000001)}
  13.     701 T@4: | | | | | | | | | | | | <row_ins_clust_index_entry_low 2649
  14.     702 T@4: | | | | | | | | | | | <row_ins_clust_index_entry 3313
  15.     703 T@4: | | | | | | | | | | | >btr_cur_search_to_nth_level
  16.     704 T@4: | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  17.     705 T@4: | | | | | | | | | | | ib_cur: delete-mark=1 sec 406:4:in b(367) by 183808
6、接下就是真正插入輔助索引

點擊(此處)折疊或打開

  1.     706 T@4: | | | | | | | | | | | >row_ins_sec_index_entry_low
  2.     707 T@4: | | | | | | | | | | | | >btr_cur_search_to_nth_level
  3.     708 T@4: | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  4.     709 T@4: | | | | | | | | | | | | >row_ins_scan_sec_index_for_duplicate
  5.     710 T@4: | | | | | | | | | | | | | >btr_cur_search_to_nth_level
  6.     711 T@4: | | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  7.     712 T@4: | | | | | | | | | | | | | >btr_cur_search_to_nth_level
  8.     713 T@4: | | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  9.     714 T@4: | | | | | | | | | | | | | >row_vers_impl_x_locked_low
  10.     715 T@4: | | | | | | | | | | | | | <row_vers_impl_x_locked_low 123
  11.     716 T@4: | | | | | | | | | | | | | >btr_cur_search_to_nth_level
  12.     717 T@4: | | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  13.     718 T@4: | | | | | | | | | | | | | >row_vers_impl_x_locked_low
  14.     719 T@4: | | | | | | | | | | | | | | info: Implicit lock is held by trx:183808
  15.     720 T@4: | | | | | | | | | | | | | <row_vers_impl_x_locked_low 329
  16.     721 T@4: | | | | | | | | | | | | <row_ins_scan_sec_index_for_duplicate 2148
  17.     722 T@4: | | | | | | | | | | | | >btr_cur_search_to_nth_level
  18.     723 T@4: | | | | | | | | | | | | <btr_cur_search_to_nth_level 2005
  19.     724 T@4: | | | | | | | | | | | | ib_cur: insert b (367) by 183808: TUPLE (info_bits=0, 2 fields): {[4] (0x00000001),[4] %(0x00000005)}
  20.     725 T@4: | | | | | | | | | | | <row_ins_sec_index_entry_low 3194
  21.     726 T@4: | | | | | | | | | | <row_upd 3066
  22.     727 T@4: | | | | | | | | | <row_upd_step 3181
  23.     728 T@4: | | | | | | | | <row_update_for_mysql_using_upd_graph 2670
  24.     729 T@4: | | | | | | | <ha_innobase::update_row 8656
注意:上面只是看trace出來的過程,很多是根據(jù)函數(shù)調(diào)用進行的猜測。

五、死鎖前事物鎖信息打印分析
打印出死鎖前事物的全部信息

點擊(此處)折疊或打開

  1. ------------------------
  2. LATEST DETECTED DEADLOCK
  3. ------------------------
  4. 2017-06-29 14:10:30 0x7fa48148b700
  5. *** (1) TRANSACTION:
  6. TRANSACTION 4912797, ACTIVE 0 sec inserting
  7. mysql tables in use 1, locked 1
  8. LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
  9. MySQL thread id 2, OS thread handle 140344520656640, query id 3371 localhost root update
  10. replace into c values(num,1)
  11. *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
  12. RECORD LOCKS space id 598 page no 4 n bits 80 index b of table `test`.`c` trx id 4912797 lock_mode X waiting
  13. Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  14.  0: len 4; hex 80000001; asc ;;
  15.  1: len 4; hex 800007d5; asc ;;

  16. *** (2) TRANSACTION:
  17. TRANSACTION 4912793, ACTIVE 0 sec updating or deleting
  18. mysql tables in use 1, locked 1
  19. 6 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 2
  20. MySQL thread id 3, OS thread handle 140344520390400, query id 3365 localhost root update
  21. replace into c values(num,1)
  22. *** (2) HOLDS THE LOCK(S):
  23. RECORD LOCKS space id 598 page no 4 n bits 80 index b of table `test`.`c` trx id 4912793 lock_mode X
  24. Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  25.  0: len 4; hex 80000001; asc ;;
  26.  1: len 4; hex 800007d5; asc ;;

  27. *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
  28. RECORD LOCKS space id 598 page no 4 n bits 80 index b of table `test`.`c` trx id 4912793 lock_mode X locks gap before rec insert intention waiting
  29. Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  30.  0: len 4; hex 80000001; asc ;;
  31.  1: len 4; hex 800007d5; asc ;;

  32. *** WE ROLL BACK TRANSACTION (1)
可以看到完全一致,證明問題判斷正確。

七、疑問
1、
二級索引中heap no運行一段時間后如下:
00200010000e 80000001800008af
002000180054 8000000180000712
00200020000e 8000000180000014
00240028002a 8000000180000017
002000300070 80000001800008e0
00200038fff2 8000000180000911
00200040002a 800000018000001a
00200048002a 800000018000001b
00000050ff82 8000000180000912
002000580000 800000018000001d
00200060ff90 800000018000001e
00200068ffd6 800000018000090e
00200070ff58 80000001800008df
很顯然這里只有8000000180000912 是當前數(shù)據(jù),其他都標記為了del,按理說長度一樣的數(shù)據(jù)進行刪除插入,空間應該
會不斷重用,為什么有時候重用不了呢?
2、在整個replace加鎖流程中,我并沒有完全搞懂,譬如182588的lock strcut(3)和lock strcut(1)分別是什么時候加
     的用于保護什么操作,這里只是從死鎖現(xiàn)象進行了分析。

八、RC模式下我做了同樣的測試得到如下的死鎖前事物LOCK STRUCT鏈表和RR模式基本無異。不在分析給出即可。
mysql> show variables like '%tx_isolation%';
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.09 sec)

點擊(此處)折疊或打開

  1. ------- TRX HAS BEEN WAITING 0 SEC FOR THIS LOCK TO BE GRANTED:
  2. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289638 lock_mode X(LOCK_X) waiting(LOCK_WAIT)
  3. Record lock, heap no 20 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  4.  0: len 4; hex 80000001; asc ;;
  5.  1: len 4; hex 80001220; asc ;;

  6. ------------------
  7. ---lock strcut(1):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  8. TABLE LOCK table `mysqlslap`.`c` trx id 289638 lock mode IX
  9. ---lock strcut(2):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  10. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289638 lock_mode X(LOCK_X) waiting(LOCK_WAIT)
  11. Record lock, heap no 20 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  12.  0: len 4; hex 80000001; asc ;;
  13.  1: len 4; hex 80001220; asc ;;

  14. ---TRANSACTION 289636, ACTIVE 0 sec updating or deleting
  15. mysql tables in use 1, locked 1
  16. 7 lock struct(s), heap size 1160, 7 row lock(s), undo log entries 2
  17. MySQL thread id 5, OS thread handle 140734658983680, query id 4646 localhost root update
  18. replace into c values(null,1)
  19. ---lock strcut(1):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  20. TABLE LOCK table `mysqlslap`.`c` trx id 289636 lock mode IX
  21. ---lock strcut(2):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  22. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289636 lock_mode X(LOCK_X)
  23. Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
  24.  0: len 8; hex 73757072656d756d; asc supremum;;
  25. Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  26.  0: len 4; hex 80000001; asc ;;
  27.  1: len 4; hex 80001221; asc !;;

  28. ---lock strcut(3):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  29. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289636 lock_mode X(LOCK_X) locks gap before rec(LOCK_GAP)
  30. Record lock, heap no 20 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  31.  0: len 4; hex 80000001; asc ;;
  32.  1: len 4; hex 80001220; asc ;;

  33. ---lock strcut(4):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  34. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289636 lock_mode X(LOCK_X)

  35. ---lock strcut(5):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  36. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289636 lock_mode X(LOCK_X)
  37. Record lock, heap no 20 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  38.  0: len 4; hex 80000001; asc ;;
  39.  1: len 4; hex 80001220; asc ;;

  40. ---lock strcut(6):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  41. RECORD LOCKS space id 407 page no 3 n bits 104 index PRIMARY of table `mysqlslap`.`c` trx id 289636 lock_mode X(LOCK_X) locks rec butnot gap(LOCK_REC_NOT_GAP)
  42. Record lock, heap no 8 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
  43.  0: len 4; hex 80001221; asc !;;
  44.  1: len 6; hex 000000046b64; asc kd;;
  45.  2: len 7; hex 30000001f00c97; asc 0 ;;
  46.  3: len 4; hex 80000001; asc ;;

  47. ---lock strcut(7):(Add by gaopeng) In modify Version I force check all REC_LOCK/TAB_LOCK for this Trx
  48. RECORD LOCKS space id 407 page no 4 n bits 104 index b of table `mysqlslap`.`c` trx id 289636 lock_mode X(LOCK_X) locks gap before rec(LOCK_GAP) insert intention(LOCK_INSERT_INTENTION) waiting(LOCK_WAIT)
  49. Record lock, heap no 20 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
  50.  0: len 4; hex 80000001; asc ;;
  51.  1: len 4; hex 80001220; asc ;;

本文某些關(guān)鍵點參考了文章,最大的提示就是自增值不是有序的,這點以后要驗證一下,但是實驗也證明了這一點:
https://yq.aliyun.com/articles/41190


作者微信:

MYSQL INNODB replace into 死鎖 及 next key lock 淺析

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI