mysql中删除同一行会经常出现死锁?太可怕了
生活随笔
收集整理的這篇文章主要介紹了
mysql中删除同一行会经常出现死锁?太可怕了
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
之前有一個(gè)同事問到我,為什么多個(gè)線程同時(shí)去做刪除同一行數(shù)據(jù)的操作,老是報(bào)死鎖,在線上已經(jīng)出現(xiàn)好多次了,我問了他幾個(gè)問題: 1. 是不是在一個(gè)事務(wù)中做了好幾件事情? 答:不是,只做一個(gè)刪除操作,自動(dòng)提交 2. 有多少個(gè)線程在做刪除? 答:差不多10個(gè) 3. 是什么隔離級(jí)別? 答:可重復(fù)讀 當(dāng)時(shí)覺得不可思議,按說自動(dòng)提交的話行鎖,如果已經(jīng)有事務(wù)加鎖了,則會(huì)等待,等提交之后再去做,發(fā)現(xiàn)已經(jīng)刪除了,就會(huì)返回,刪除0條,為什么會(huì)死鎖? 但事情已經(jīng)出了,必須研究一下,不然終究是心頭之苦啊。 然后想到既然線上有這么簡(jiǎn)單的操作就可以死鎖,那么應(yīng)該寫個(gè)簡(jiǎn)單的程序就可以重現(xiàn),然后同事李潤(rùn)杰兄弟咔嚓咔嚓沒多時(shí)就給我了,在這里謝謝他。 首先環(huán)境是這樣的:
CREATE TABLE `abcdefg` (
? `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
? `abc` varchar(30),
? `def` varchar(30) ,
? `ghi` date,
? `jkl` date,
? `mnp` tinyint(4),
? PRIMARY KEY (`id`),
? UNIQUE KEY `uniqdefghijkl` (`def`,`ghi`,`jkl`)
); 這個(gè)表包括2個(gè)索引,一個(gè)是聚簇索引,另一個(gè)是uniqdefghijkl的二級(jí)唯一索引。 事先插入很多數(shù)據(jù),然后3個(gè)線程同時(shí)做對(duì)同一條記錄的刪除,這里只做刪除操作,并且都是自動(dòng)提交,為了得到一批要?jiǎng)h除的數(shù)據(jù),事先查詢很多條出來備用。 刪除語句是這樣的: delete from?abcdefg?WHERE abc= '我是變量'?and def= '我是變量'?and ghi= '2013-12-19 00:00:00'?and jkl= '2013-12-20 00:00:00';
那么現(xiàn)在就開始重現(xiàn)。。。 果然很快,死鎖真的出現(xiàn)了,下面是執(zhí)行show engine innodb status的結(jié)果: =================================================== LATEST DETECTED DEADLOCK
------------------------
140123 12:20:50
*** (1) TRANSACTION:
TRANSACTION 2E10, ACTIVE 4917 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x1008, query id 43 192.168.xx.x username upd
ating
delete from?abcdefg?WHERE abc= '我是變量'?and def= '我是變量'?and ghi= '2013-12-19 00:00:00'?and jkl= '2013-12-20 00:00:00'; *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table `deadlock`.`abcdefg` trx id 2E10 lock_mode X locks rec but not gap waiti ng
Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;?
*** (2) TRANSACTION:
TRANSACTION 2E0E, ACTIVE 4917 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 1, OS thread handle 0x1190, query id 41 192.168.xx.xx?username?upd ating
delete from?abcdefg?WHERE abc= '我是變量'?and def= '我是變量'?and ghi= '2013-12-19 00:00:00'?and jkl= '2013-12-20 00:00:00'; *** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table `deadlock`.`abcdefg` trx id 2E0E lock_mode X locks rec but not gap Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;?
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table `deadlock`.`abcdefg` trx id 2E0E lock_mode X waiting Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;?
*** WE ROLL BACK TRANSACTION (1) =================================================== 這是在三個(gè)線程的情況下是可以重現(xiàn)死鎖的,但是為了更容易調(diào)試,試了一下在2個(gè)線程的情況下如何,最終發(fā)現(xiàn)重現(xiàn)不了。 這下壞了,多線程調(diào)試很麻煩,有時(shí)候這個(gè)走那個(gè)不走的,如果凍結(jié)某個(gè)線程,有可能導(dǎo)致線程之間死鎖,或者自然執(zhí)行,那又不能出現(xiàn)死鎖的情況,因?yàn)檫@個(gè)死鎖也是偶然性的,所以最終只有一種方法,那就是在mysql代碼中打印log信息,將鎖、記錄與事務(wù)這塊的函數(shù)中具有分歧點(diǎn)的地方都加了注釋,并且將有用的信息打印出來,最終分析log文件,才發(fā)現(xiàn)了真正死鎖的貓膩。 現(xiàn)在將三個(gè)導(dǎo)致死鎖的事務(wù)的時(shí)序圖畫出來:
圖1 說明: 上面的數(shù)字都是源代碼中的關(guān)于各種鎖的位圖值: LOCK_TABLE:16 LOCK_IX:1 LOCK_REC_NOT_GAP:1024 LOCK_WAIT:256 LOCK_REC:32 LOCK_X:3 ? 所以鎖@6表示的是LOCK_REC |?LOCK_REC_NOT_GAP |?LOCK_X |?LOCK_WAIT = 1315 依次類推 ? 這里檢查死鎖的算法大體上說一下,無非是檢查有沒有形成等待環(huán) 事務(wù)B的鎖@8等待事務(wù)C的鎖@6,事務(wù)C的鎖@6在等待事務(wù)B的鎖@3,此時(shí)發(fā)現(xiàn)又繞回來了,那么產(chǎn)生死鎖。 到這里,死鎖的現(xiàn)象如何產(chǎn)生已經(jīng)解釋清楚,但是,這是為什么呢? 這里的疑問是: 在事務(wù)A提交之后,將事務(wù)B喚醒了,此時(shí)事務(wù)B的鎖@4為REC NOTGAP X(1059),那么此時(shí)這個(gè)事務(wù)又去檢查鎖的情況,看看自己事務(wù)的鎖有沒有GRANT成功的,如果有則直接使用并且繼續(xù)執(zhí)行,如果沒有則再加鎖,做這個(gè)檢查的函數(shù)為lock_rec_has_expl,它做的事情是下面的檢查: =========================================================== ? ? ? ? ? ? ? ? lock = lock_rec_get_first(block, heap_no); ?????????????????while?(lock) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??if?(lock->trx == trx ??????????????????????????????????? && !lock_is_wait_not_by_other(lock->type_mode) ??????????????????????????????????? && lock_mode_stronger_or_eq(lock_get_mode(lock), ????????????????????????????????????????????????????????????????????????????????????????????????precise_mode & LOCK_MODE_MASK) ??????????????????????????????????? && (!lock_rec_get_rec_not_gap(lock) ????????????????????????????????????????????????|| (precise_mode & LOCK_REC_NOT_GAP) ????????????????????????????????????????????????|| heap_no == PAGE_HEAP_NO_SUPREMUM) ??????????????????????????????????? && (!lock_rec_get_gap(lock) ????????????????????????????????????????????????|| (precise_mode & LOCK_GAP) ????????????????????????????????????????????????|| heap_no == PAGE_HEAP_NO_SUPREMUM) ??????????????????????????????????? && (!lock_rec_get_insert_intention(lock))) { ? ?????????????????????????????????????????????????return(lock); ????????????????????????????????} ? ????????????????????????????????lock = lock_rec_get_next(heap_no, lock); ????????????????} ============================================================= 這里需要滿足6個(gè)條件: 首先這個(gè)鎖是自己事務(wù)的 這個(gè)鎖不是處于等待狀態(tài) 當(dāng)前鎖的類型與precise_mode是兼容的,precise_mode值是X鎖,因?yàn)檫@里是要做刪除 當(dāng)前鎖不是NOT GAP類型,或者要加的鎖類型是NOTGAP類型的,或者h(yuǎn)eapno為1 當(dāng)前鎖不是GAP類型,或者要加的鎖類型是GAP類型的,或者h(yuǎn)eapno為1 當(dāng)前鎖不是意向插入鎖 但此時(shí)發(fā)現(xiàn)1059(鎖@4)根本不滿足第4點(diǎn)啊,因?yàn)樗紫仁荖OTGAP鎖,同時(shí)heapno不是1,所以沒有找到,所以在外面又重新創(chuàng)建一個(gè)鎖,因?yàn)榇藭r(shí)這行已經(jīng)有鎖了,那么它會(huì)創(chuàng)建一個(gè)REC WAIT X鎖(291),也就是鎖@8。 ? 所以即使鎖@4不是處于等待狀態(tài)了,此時(shí)也不能直接執(zhí)行呢,而是重新創(chuàng)建了一個(gè)鎖。此時(shí)導(dǎo)致了死鎖。 那么現(xiàn)在問題又來了,從上圖可以看到,這個(gè)時(shí)間序列沒有什么特別的,或者特殊的一個(gè)交叉過程,從而是不是我們可以很容易的重現(xiàn)呢?僅僅通過開啟三個(gè)會(huì)話,都設(shè)置為not autocommit的,因?yàn)樾枰獙⒌谝粋€(gè)事務(wù)A的提交放在事務(wù)B C的后面。 那么開始了,創(chuàng)建相同的表,刪除同一行記錄。
圖2 按說,上面這個(gè)圖與圖1沒有什么區(qū)別,但沒有死鎖?為什么? 其實(shí)沒有死鎖是正常的,如果這樣就死鎖了,那mysql簡(jiǎn)直不能用了!!! 看來還是有區(qū)別的 正常模式下再做一次log分析,從log中看出了大問題...... 再將上面詳細(xì)的加鎖圖在無死鎖模式下的情況貼出來:
圖3 此時(shí)發(fā)現(xiàn),圖3其實(shí)與圖1是一樣的,那為什么圖3可以正常執(zhí)行完成,而圖1死鎖了呢? 但認(rèn)真仔細(xì)看了之后,發(fā)現(xiàn)有很小的地方是不同的,圖3中的鎖@4加上的鎖是291(REC & X & WAIT),而圖1中加的鎖比它多了一個(gè)NOTGAP的鎖,鎖@6也是一樣的,圖3的事務(wù)A在提交并且喚醒了鎖@4之后,它的鎖類型為REC+X(35),而圖1中的值也是比它多了一個(gè)NOTGAP鎖。 現(xiàn)在已經(jīng)基本定位了問題所在,應(yīng)該是NOTGAP搞的鬼。但是為什么會(huì)有差別呢? 此時(shí)還需要回到代碼中查看,通過日志分析,發(fā)現(xiàn)2個(gè)在執(zhí)行下面代碼時(shí)走了不同的路: ======================================= ? ? ? ? ? ? ? ???if?(prebuilt->select_lock_type != LOCK_NONE) { ????????????????????????????????ulint????????lock_type; ? ?????????????????????????????????if?(!set_also_gap_locks ??????????????????????????????????? || srv_locks_unsafe_for_binlog ??????????????????????????????????? || trx->isolation_level <= TRX_ISO_READ_COMMITTED ??????????????????????????????????? || (unique_search ????????????????????????????????????????????????&& !UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp)))) { ?????????????????????????????????????????????????goto?no_gap_lock;//直接路到下面?lock_type = LOCK_REC_NOT_GAP;處 ????????????????????????????????}?else?{ ????????????????????????????????????????????????lock_type = LOCK_ORDINARY; ????????????????????????????????} 、 ?????????????????????????????????if?(index == clust_index ??????????????????????????????????? && mode == PAGE_CUR_GE ??????????????????????????????????? && direction == 0 ??????????????????????????????????? && dtuple_get_n_fields_cmp(search_tuple) ??????????????????????????????????? == dict_index_get_n_unique(index) ??????????????????????????????????? && 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) { no_gap_lock://標(biāo)記 ????????????????????????????????????????????????lock_type = LOCK_REC_NOT_GAP; ????????????????????????????????} ?======================================= 這里關(guān)鍵的分叉口就是在上面紅色字體部分,死鎖的時(shí)候走了goto?no_gap_lock,而沒有出現(xiàn)死鎖的時(shí)候走的是lock_type = LOCK_ORDINARY;,而LOCK_ORDINARY表示的是0,什么都沒有,所以這2條路的不同就是差1024(NOTGAP鎖)。 那么從日志中發(fā)現(xiàn),走了第一條路是因?yàn)闂l件(unique_search&& !UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))是符合的。rec_get_deleted_flag函數(shù)的作用是判斷這條記錄是不是已經(jīng)打了刪除標(biāo)志。 現(xiàn)在豁然明白了,如果當(dāng)前這條要加鎖的記錄還沒有打刪除標(biāo)志,則加的鎖是NOTGAP類型的鎖,否則就不設(shè)置類型,那說明上面的圖1中事務(wù)A還是有一個(gè)細(xì)節(jié)沒有畫出來,正因?yàn)檫@個(gè)細(xì)節(jié)與事務(wù)B發(fā)生了交叉,導(dǎo)致了事務(wù)B在做的時(shí)候還沒有打了刪除標(biāo)記,所以就加了NOTGAP鎖,所以導(dǎo)致后面的死鎖。 而正常情況下,也就是圖2的測(cè)試,因?yàn)槭聞?wù)A已經(jīng)完成了所有的操作,只等待提交,此時(shí)肯定已經(jīng)打了刪除標(biāo)志,則在加鎖時(shí)不會(huì)加NOTGAP鎖,所以就不會(huì)出現(xiàn)死鎖。 哎,用一句同事常說的話:我這下真的了然了,原來問題這么復(fù)雜,mysql中的貓膩太多了。 那現(xiàn)在分析一下原因吧: 現(xiàn)在已經(jīng)確定問題就是出現(xiàn)在上面代碼的判斷中,在上面代碼的上面還有一段注釋: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???/* Try to place a lock on the index record; note that delete ????????????????????????????????marked records are a special case in a unique search. If there ????????????????????????????????is a non-delete marked record, then it is enough to lock its ????????????????????????????????existence with LOCK_REC_NOT_GAP. */ ? 這說明了加NOTGAP鎖的意圖,說明上面代碼的判斷是專門做的,具體原因就無從查起了,但是注釋中說這是一種特殊情況,為什么呢?解決方式是把那2行直接去掉就可以了(測(cè)試過不會(huì)出現(xiàn)死鎖了),但這個(gè)會(huì)不會(huì)是解決問題的根本原因,還要等待官方人員的處理。 所以到這里,把完整的死鎖圖貼上來:
思維發(fā)散: 1. 對(duì)于已經(jīng)刪除的記錄(已經(jīng)提交,但還沒有purge),如果再去做刪除操作,則此時(shí)還會(huì)加鎖么?加什么鎖?(這個(gè)問題,由于時(shí)間太緊,后面再給出驗(yàn)證說明,如果有興趣,自己也可以做一下的) 2. 這個(gè)問題是在隔離級(jí)別是可重復(fù)讀的情況下存在的,但如果是其它情況下會(huì)出現(xiàn)么? 3. 如果是根據(jù)主鍵刪除,這個(gè)問題還會(huì)出現(xiàn)么? 總結(jié):在mysql中,其實(shí)很多東西都不能按照常理來想的,這個(gè)問題本來在達(dá)夢(mèng)與oracle中是根本不可想象的,根本不會(huì)出現(xiàn)的,所以才有一開始覺得不可能的感覺,最后才發(fā)現(xiàn),原來是真的。 在這里感謝一下同事們的幫助與討論,感謝勇哥,杰哥
CREATE TABLE `abcdefg` (
? `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
? `abc` varchar(30),
? `def` varchar(30) ,
? `ghi` date,
? `jkl` date,
? `mnp` tinyint(4),
? PRIMARY KEY (`id`),
? UNIQUE KEY `uniqdefghijkl` (`def`,`ghi`,`jkl`)
); 這個(gè)表包括2個(gè)索引,一個(gè)是聚簇索引,另一個(gè)是uniqdefghijkl的二級(jí)唯一索引。 事先插入很多數(shù)據(jù),然后3個(gè)線程同時(shí)做對(duì)同一條記錄的刪除,這里只做刪除操作,并且都是自動(dòng)提交,為了得到一批要?jiǎng)h除的數(shù)據(jù),事先查詢很多條出來備用。 刪除語句是這樣的: delete from?abcdefg?WHERE abc= '我是變量'?and def= '我是變量'?and ghi= '2013-12-19 00:00:00'?and jkl= '2013-12-20 00:00:00';
那么現(xiàn)在就開始重現(xiàn)。。。 果然很快,死鎖真的出現(xiàn)了,下面是執(zhí)行show engine innodb status的結(jié)果: =================================================== LATEST DETECTED DEADLOCK
------------------------
140123 12:20:50
*** (1) TRANSACTION:
TRANSACTION 2E10, ACTIVE 4917 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x1008, query id 43 192.168.xx.x username upd
ating
delete from?abcdefg?WHERE abc= '我是變量'?and def= '我是變量'?and ghi= '2013-12-19 00:00:00'?and jkl= '2013-12-20 00:00:00'; *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table `deadlock`.`abcdefg` trx id 2E10 lock_mode X locks rec but not gap waiti ng
Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;?
*** (2) TRANSACTION:
TRANSACTION 2E0E, ACTIVE 4917 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 1, OS thread handle 0x1190, query id 41 192.168.xx.xx?username?upd ating
delete from?abcdefg?WHERE abc= '我是變量'?and def= '我是變量'?and ghi= '2013-12-19 00:00:00'?and jkl= '2013-12-20 00:00:00'; *** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table `deadlock`.`abcdefg` trx id 2E0E lock_mode X locks rec but not gap Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;?
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table `deadlock`.`abcdefg` trx id 2E0E lock_mode X waiting Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;?
*** WE ROLL BACK TRANSACTION (1) =================================================== 這是在三個(gè)線程的情況下是可以重現(xiàn)死鎖的,但是為了更容易調(diào)試,試了一下在2個(gè)線程的情況下如何,最終發(fā)現(xiàn)重現(xiàn)不了。 這下壞了,多線程調(diào)試很麻煩,有時(shí)候這個(gè)走那個(gè)不走的,如果凍結(jié)某個(gè)線程,有可能導(dǎo)致線程之間死鎖,或者自然執(zhí)行,那又不能出現(xiàn)死鎖的情況,因?yàn)檫@個(gè)死鎖也是偶然性的,所以最終只有一種方法,那就是在mysql代碼中打印log信息,將鎖、記錄與事務(wù)這塊的函數(shù)中具有分歧點(diǎn)的地方都加了注釋,并且將有用的信息打印出來,最終分析log文件,才發(fā)現(xiàn)了真正死鎖的貓膩。 現(xiàn)在將三個(gè)導(dǎo)致死鎖的事務(wù)的時(shí)序圖畫出來:
| 事務(wù)A | 事務(wù)B | 事務(wù)C |
| 開始 | ? | ? |
| 表的IX鎖 17 ? ?@1 | ? | ? |
| 二級(jí)索引行鎖X REC NOTGAP 1059 ? ?@2 檢查死鎖 沒事 | ? | ? |
| ? | 表IX鎖 17 ? ?@3 | ? |
| ? | 二級(jí)索引記錄行鎖 REC NOTGAP X WAIT 1315 ? ?@4 檢查死鎖,沒事 | ? |
| ? | ? | 表IX鎖 17 ? ?@5 |
| ? | ? | 二級(jí)索引記錄行鎖 REC NOTGAP X WAIT 1315 ? ?@6 檢查死鎖 沒事 |
| 聚簇索引行鎖X REC NOTGAP 1059 ? ?@7 | ? | ? |
| ? | wait.... suspend.... | wait.... suspend.... |
| commit | ? | ? |
| ? | wakeup this trx 將@4的WAIT去掉,成為1059 | ? |
| ? | 二級(jí)索引記錄行鎖 REC X WAIT 291 ? ?@8 檢查死鎖 發(fā)現(xiàn)死鎖 | ? |
| 事務(wù)A | 事務(wù)B | 事務(wù)C |
| begin | ? | ? |
| delete 刪除行數(shù)返回為1 | ? | ? |
| ? | begin | ? |
| ? | delete 阻塞 | ? |
| ? | ? | begin |
| ? | ? | 阻塞 |
| commit | ? | ? |
| ? | 觀察有沒有死鎖 其實(shí)并沒有死鎖 刪除行數(shù)返回為0 | ? |
| ? | ? | 刪除行數(shù)返回為0 |
| 事務(wù)A | 事務(wù)B | 事務(wù)C |
| 開始 | ? | ? |
| 表的IX鎖 17 ? ?@1 | ? | ? |
| 二級(jí)索引行鎖X REC NOTGAP 1059 ? ?@2 檢查死鎖 沒事 | ? | ? |
| 聚簇索引行鎖X REC NOTGAP 1059 ? ?@7 檢查死鎖 沒事 | ? | ? |
| ? | 表IX鎖 17 ? ?@3 | ? |
| ? | 二級(jí)索引記錄行鎖 REC X WAIT?291 ? ?@4 檢查死鎖,沒事 | ? |
| ? | ? | 表IX鎖 17 ? ?@5 |
| ? | ? | 二級(jí)索引記錄行鎖 REC X WAIT?291?@6 檢查死鎖 沒事 |
| ? | wait.... suspend.... | wait.... suspend.... |
| commit | ? | ? |
| ? | wakeup this trx 將@4的WAIT去掉,成為35 | ? |
| ? | 執(zhí)行完成,提交 | ? |
| ? | ? | 執(zhí)行完成 |
| 事務(wù)A | 事務(wù)B | 事務(wù)C |
| 開始 | ? | ? |
| 表的IX鎖 17 ? ?@1 | ? | ? |
| 二級(jí)索引行鎖X REC NOTGAP 1059 ? ?@2 檢查死鎖 沒事 | ? | ? |
| ? | 表IX鎖 17 ? ?@3 | ? |
| ? | 二級(jí)索引記錄行鎖 REC NOTGAP X WAIT 1315 ? ?@4 檢查死鎖,沒事 | ? |
| ? | ? | 表IX鎖 17 ? ?@5 |
| ? | ? | 二級(jí)索引記錄行鎖 REC NOTGAP X WAIT 1315 ? ?@6 檢查死鎖 沒事 |
| 對(duì)二級(jí)索引記錄加刪除標(biāo)志(這個(gè)是最關(guān)鍵的) 這個(gè)交叉點(diǎn)就是:在鎖@2與@7之間,有事務(wù)B加了鎖@4,事務(wù)加了鎖@6 | ? | ? |
| 聚簇索引行鎖X REC NOTGAP 1059 ? ?@7 | ? | ? |
| ? | wait.... suspend.... | wait.... suspend.... |
| commit | ? | ? |
| ? | wakeup this trx 將@4的WAIT去掉,成為1059 | ? |
| ? | 二級(jí)索引記錄行鎖 REC X WAIT 291 ? ?@8 檢查死鎖 發(fā)現(xiàn)死鎖 | ? |
思維發(fā)散: 1. 對(duì)于已經(jīng)刪除的記錄(已經(jīng)提交,但還沒有purge),如果再去做刪除操作,則此時(shí)還會(huì)加鎖么?加什么鎖?(這個(gè)問題,由于時(shí)間太緊,后面再給出驗(yàn)證說明,如果有興趣,自己也可以做一下的) 2. 這個(gè)問題是在隔離級(jí)別是可重復(fù)讀的情況下存在的,但如果是其它情況下會(huì)出現(xiàn)么? 3. 如果是根據(jù)主鍵刪除,這個(gè)問題還會(huì)出現(xiàn)么? 總結(jié):在mysql中,其實(shí)很多東西都不能按照常理來想的,這個(gè)問題本來在達(dá)夢(mèng)與oracle中是根本不可想象的,根本不會(huì)出現(xiàn)的,所以才有一開始覺得不可能的感覺,最后才發(fā)現(xiàn),原來是真的。 在這里感謝一下同事們的幫助與討論,感謝勇哥,杰哥
轉(zhuǎn)載于:https://www.cnblogs.com/bamboos/p/3532150.html
總結(jié)
以上是生活随笔為你收集整理的mysql中删除同一行会经常出现死锁?太可怕了的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GeoServer 数据导出
- 下一篇: MyEclipse数据库连接配置(以Or