如何解決的RR模式下死鎖一列,針對這個(gè)問(wèn)題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
CREATE TABLE `t8` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `d_id` varchar(40) NOT NULL DEFAULT '', `b_id` varchar(40) NOT NULL DEFAULT '', `is_dropped` tinyint(1) NOT NULL DEFAULT '0', `u_c` varchar(10) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `DealerAndBrokerAndDropped` (`d_id`,`b_id`,`is_dropped`) ) ENGINE=InnoDB ; insert into t8 values(1,1,1,0,'a'); insert into t8 values(2,2,2,0,'a'); insert into t8 values(3,3,3,0,'a'); insert into t8 values(4,4,4,0,'a'); insert into t8 values(5,5,5,0,'a'); insert into t8 values(6,6,6,0,'a'); insert into t8 values(7,7,7,0,'a'); insert into t8 values(8,8,8,0,'a'); insert into t8 values(9,9,9,0,'a'); insert into t8 values(10,10,10,0,'a'); insert into t8 values(11,11,11,0,'a');
執行語(yǔ)句如下:
|S1|S2|
|-|-|
|begin||
|select u_c from t8 where d_id=’1’ and b_id=’1’ and is_dropped=0 for update;||
||select u_c from t8 where d_id=’1’ and b_id=’1’ and is_dropped=0 for update; 處于堵塞狀態(tài)|
|update t8 set u_c=’b’ where d_id=’1’ and b_id=’1’; —此時(shí)觸發(fā)死鎖 S2回滾||
發(fā)生死鎖記錄如下:
仔細分析我們會(huì )發(fā)現trx id 5679最后被堵塞需要獲取的鎖為(lock_mode X waiting),堵塞發(fā)生在索引DealerAndBrokerAndDropped 上,也就是這是一個(gè)next key lock 且需要獲取的模式為L(cháng)OCK_X,處于等待狀態(tài)。
而我們來(lái)看trx id 5679前面獲取的鎖是什么呢?顯然可以看到為(lock_mode X locks rec but not gap),獲取發(fā)生在索引DealerAndBrokerAndDropped 上,也就是這是一個(gè)key lock且獲取模式為L(cháng)OCK_X。
但是我們需要知道DealerAndBrokerAndDropped明明是一個(gè)唯一索引,獲取key lock我們很容易理解,但是為什么也會(huì )出現獲取next key lock呢?這個(gè)問(wèn)題我們先放一下,先來(lái)分析一下整個(gè)死鎖的產(chǎn)生的過(guò)程
S1(select操作)
通過(guò)唯一性索引定位索引數據獲取了唯一索引DealerAndBrokerAndDropped 上的
LOCK_REC_NOT_GAP|LOCK_X,獲取成功記錄就是 d_id=’1’ b_id=’1’ is_dropped=0這條數據。
S1(select操作)
回表獲取全部數據,這個(gè)時(shí)候需要主鍵上的相應的行鎖。LOCK_REC_NOT_GAP|LOCK_X獲取成功
S2(select操作)
通過(guò)唯一性索引定位索引數據試圖獲取了唯一索引DealerAndBrokerAndDropped 上的
LOCK_REC_NOT_GAP|LOCK_X,獲取失敗記錄就是 d_id=’1’ b_id=’1’ is_dropped=0這條數據,處于等待狀態(tài)。
S1(update操作)
通過(guò)索引DealerAndBrokerAndDropped 查找數據(注意這里已經(jīng)不是唯一性定位操作了,下面會(huì )做分析),這個(gè)時(shí)候首先需要通過(guò)查詢(xún)條件獲取出需要更新的第一條數據,實(shí)際上這個(gè)時(shí)候也是d_id=’1’ b_id=’1’ is_dropped=0這條數據,需要獲取的鎖為L(cháng)OCK_ORDINARY[next_key_lock]|LOCK_X,這個(gè)時(shí)候我發(fā)現雖然S1之前獲取了這條數據的鎖,但是鎖模式變化了(一致不會(huì )重新獲取,下面會(huì )分析這種行為),因此這里需要重新獲取,但是這顯然是不行的,因為S2都還處于等待中,因此這里也發(fā)生了等待。
因此通過(guò)這個(gè)過(guò)程就出現死鎖,S2等S1 S1等S2。
關(guān)于這里我們參考函數lock_rec_lock_fast,這里會(huì )不進(jìn)行行鎖沖突驗證而進(jìn)行快速加鎖,如果鎖模式?jīng)]有變化則也會(huì )再這里進(jìn)行快速加鎖(也就是直接跳過(guò)),當然如果塊中一個(gè)row lock 都沒(méi)有也會(huì )在這里進(jìn)行加鎖,這是每個(gè)加行鎖的操作都必須經(jīng)歷的判斷,如果不能快速加鎖則進(jìn)入slow加鎖方式,這里看一下下面的這段代碼:
if (lock_rec_get_next_on_page(lock) || lock->trx != trx || lock->type_mode != (mode | LOCK_REC) || lock_rec_get_n_bits(lock) <= heap_no) { status = LOCK_REC_FAIL; }
這里的lock->trx != trx會(huì )判斷本次加鎖事務(wù)和上次加鎖事務(wù)是否是同一個(gè)事務(wù),lock->type_mode != (mode | LOCK_REC)會(huì )判斷鎖模式是否相同。如果不能滿(mǎn)足條件則判定為L(cháng)OCK_REC_FAIL,進(jìn)入slow加鎖方式。
而我們這里S1加鎖第一次是LOCK_REC_NOT_GAP|LOCK_X,而第二次是LOCK_ORDINARY[next_key_lock]|LOCK_X,顯然變化了,因此進(jìn)入slow加鎖階段,進(jìn)行沖突驗證,結果嘛也就沖突了。這是本死鎖的一個(gè)原因。
這是本死鎖的一個(gè)最重要原因,知道了這個(gè)原因這個(gè)案例就理解了。首先我們先看這個(gè)update語(yǔ)句:
update t8 set u_c='b' where d_id='1' and b_id='1';
我們發(fā)現這個(gè)時(shí)候唯一索引還少一個(gè)條件也就是is_dropped字段,這個(gè)時(shí)候本次定位查詢(xún)不會(huì )判定為唯一性查詢(xún),而是普通的二級索引定位方式,這個(gè)時(shí)候RR模式出現LOCK_ORDINARY[next_key_lock]就顯得很自然了,下面是這個(gè)判斷過(guò)程,代碼位于row_search_mvcc中。
(match_mode == ROW_SEL_EXACT && dict_index_is_unique(index) && dtuple_get_n_fields(search_tuple) == dict_index_get_n_unique(index) && (dict_index_is_clust(index) || !dtuple_contains_null(search_tuple)))
稍微解釋一下,唯一性查找條件至少包含如下3點(diǎn):
索引具有唯一性
查詢(xún)的字段數量和索引唯一性字段數量相同
是主鍵或者查詢(xún)條件中不包含NULL值
注意第3點(diǎn)源碼說(shuō)明如下:
/* Note above that a UNIQUE secondary index can contain many rows with the same key value if one of the columns is the SQL null. A clustered index under MySQL can never contain null columns because we demand that all the columns in primary key are non-null. */
滿(mǎn)足上面4點(diǎn)條件才能確認為唯一查找,本查詢(xún)由于第3條不滿(mǎn)足因此,因此判定失敗。
不僅如此如果本條數據加鎖成功,那么你會(huì )看到如下的結果:
---TRANSACTION 25830, ACTIVE 2 sec 4 lock struct(s), heap size 1160, 3 row lock(s), undo log entries 1 MySQL thread id 5, OS thread handle 140737101231872, query id 4115 localhost root starting show engine innodb status TABLE LOCK table `test`.`t8` trx id 25830 lock mode IX RECORD LOCKS space id 1050 page no 4 n bits 80 index DealerAndBrokerAndDropped of table `test`.`t8` trx id 25830 lock_mode X Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 1; hex 31; asc 1;; 1: len 1; hex 31; asc 1;; 2: len 1; hex 80; asc ;; 3: len 8; hex 8000000000000001; asc ;; RECORD LOCKS space id 1050 page no 3 n bits 80 index PRIMARY of table `test`.`t8` trx id 25830 lock_mode X locks rec but not gap Record lock, heap no 2 PHYSICAL RECORD: n_fields 7; compact format; info bits 0 0: len 8; hex 8000000000000001; asc ;; 1: len 6; hex 0000000064e6; asc d ;; 2: len 7; hex 5f000000430110; asc _ C ;; 3: len 1; hex 31; asc 1;; 4: len 1; hex 31; asc 1;; 5: len 1; hex 80; asc ;; 6: len 1; hex 62; asc b;; RECORD LOCKS space id 1050 page no 4 n bits 80 index DealerAndBrokerAndDropped of table `test`.`t8` trx id 25830 lock_mode X locks gap before rec Record lock, heap no 11 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 2; hex 3130; asc 10;; 1: len 2; hex 3130; asc 10;; 2: len 1; hex 80; asc ;; 3: len 8; hex 800000000000000a; asc ;;
我們發(fā)現DealerAndBrokerAndDropped唯一索引的嚇一跳記錄也加了gap lock,這完全是RR模式非唯一索引的加鎖行為。
如果我們將語(yǔ)句
update t8 set u_c='b' where d_id='1' and b_id='1';
修改為
update t8 set u_c='b' where d_id='1' and b_id='1' and is_dropped=0;
那么死鎖將不會(huì )觸發(fā)了。原因就是第三部分我們說(shuō)的,這里鎖模式完全一致,不會(huì )導致加鎖操作了。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站