天天看點

死鎖摘錄【yetdone】

盡量避免事務,縮小事務範圍(避免gap鎖)(個人認為)

按表順序,記錄id順序依次通路

直接申請足夠級别的鎖,即排他鎖,而不應先申請共享鎖,更新時再申請排他鎖,因為當使用者申請排他鎖時,其他事務可能又已經獲得了相同記錄的共享鎖,進而造成鎖沖突,甚至死鎖。

在repeatable-read隔離級别下,如果兩個線程同時對相同條件記錄用select...for update加排他鎖,在沒有符合該條件記錄情況下,兩個線程都會加鎖成功。程式發現記錄尚不存在,就試圖插入一條新記錄,如果兩個線程都這麼做,就會出 現死鎖。這種情況下,将隔離級别改成read committed,就可避免問題

     這種情況比較隐晦,事務a在執行時,除了在二級索引加鎖外,還會在聚簇索引上加鎖,在聚簇索引上加鎖的順序是[1,4,2,3,5],而事務b執行時,隻在聚簇索引上加鎖,加鎖順序是[1,2,3,4,5],這樣就造成了死鎖的可能性。

死鎖摘錄【yetdone】

降低隔離級别。如果業務允許,将隔離級别調低也是較好的選擇,比如将隔離級别從rr調整為rc,可以避免掉很多因為gap鎖造成的死鎖——與上述死鎖案例無關

mysql事務死鎖案例分析(gap鎖篇) 

官方文檔

下面是mysql對gap lock給出的官方解釋:

a gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record。

在官方詳細解析中有提到一個要點,也是導緻本次事務死鎖的根本原因

gap locks in ​<code>​innodb​</code>​ are “purely inhibitive”, which means they only stop other transactions from inserting to the gap. they do not prevent different transactions from taking gap locks on the same gap. thus, a gap x-lock has the same effect as a gap s-lock.

gap lock不排斥其他gap lock,但排斥插入意向鎖

大緻意思是:x-lock的gap和s-lock的效果是一樣的,都是可以被多個事務擷取到。

下面開始先分析gap lock 在唯一索引中的表現

以上為mysql中,innodb下,建立唯一索引,在執行sql中存在索引不命中時使用了gap鎖導緻的死鎖場景分析,主要原因還是在于gap鎖可以被多個事務擷取到,謝謝拍磚^_^

delete的where子句沒有滿足條件的記錄,而對于不存在的記錄 并且在rr級别下,delete加鎖類型為gap lock,gap lock之間是相容的,是以兩個事務都能成功執行delete;關于gap lock可以參考文章​​​​這裡的gap範圍是索引a列(3,5)的範圍。

insert時,其加鎖過程為先在插入間隙上擷取插入意向鎖,插入資料後再擷取插入行上的排它鎖。又插入意向鎖與gap lock和 next-key lock沖突,即一個事務想要擷取插入意向鎖,如果有其他事務已經加了gap lock或 next-key lock,則會阻塞。

intention shared (is): transaction t intends to set s locks on individual rows in table t.

intention exclusive (ix): transaction t intends to set x locks on those rows.

before a transaction can acquire an s lock on a row in table t, it must first acquire an is or stronger lock on t. before a transaction can acquire an x lock on a row, it must first acquire an ix lock on t.

the main purpose of ix and is locks is to show that someone is locking a row, or going to lock a row in the table.

處理行鎖和表鎖之間的沖突,用于表明“某個事務正在某一行上持有了鎖,或者準備去持有鎖”。

<col>

x

ix

s

is

​<code>​沖突​</code>​

相容

預設(rr)情況下,innodb使用next-key locks來鎖定記錄。

但當查詢的索引含有唯一屬性的時候,next-key lock 會進行優化,将其降級為record lock,即僅鎖住索引本身,不是範圍。

an insert intention lock is a type of gap lock set by insert operations prior to row insertion. this lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. suppose that there are index records with values of 4 and 7. separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

注:插入意向鎖并非意向鎖,而是一種特殊的間隙鎖。(隻是名字叫“意向”?)

gap

insert intention

record

next-key

表注:橫向是已經持有的鎖,縱向是正在請求的鎖。

本文提出了2個經典死鎖案例:

1 duplicate primary key

2 gap與insert intention

但本文解釋不如人意

y

我先把官方文檔對于insert 加鎖的描述貼出來

insert sets an exclusive lock on the inserted row. this lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.prior to inserting the row, a type of gap lock called an insertion intention gap lock is set. this lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.if a duplicate-key error occurs, a shared lock on the duplicate index record is set. this use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock. 

大體的意思是:insert會對插入成功的行加上排它鎖,這個排它鎖是個記錄鎖,而非next-key鎖(當然更不是gap鎖了),不會阻止其他并發的事務往這條記錄之前插入記錄。在插入之前,會先在插入記錄所在的間隙加上一個插入意向gap鎖(簡稱i鎖吧),并發的事務可以對同一個gap加i鎖。如果insert 的事務出現了duplicate-key error ,事務會對duplicate index record加共享鎖。這個共享鎖在并發的情況下是會産生死鎖的,比如有兩個并發的insert都對要對同一條記錄加共享鎖,而此時這條記錄又被其他事務加上了排它鎖,排它鎖的事務送出或者復原後,兩個并發的insert操作是會發生死鎖的。

幾個例子

1)select for update未命中唯一索引 gap鎖阻塞插入意向鎖

2)插入意向鎖互相不阻塞

3) insert 行記錄排他鎖阻塞select in share mode

這裡我們清楚的知道 --“注意 t1 的gap,insert intention ,t2 的gap 都是鎖的同一個地方 “space id 0 page no 198 n bits 80””—3個鎖鎖住同一個地方的原因了。因為customer_id = 3 和customer_id =5 都是屬于同一個gap(2,6)。

t1 持有 gap (2,6) x鎖,同時有個 insert intention (2,6)的x鎖在等待gap(2,6)的x鎖的釋放;

t2 持有 gap(2,6) x鎖。

這就是導緻t1的insert 語句執行不下去的真正原因。 當t2的insert 語句執行的時候,(即f語句)可以預見,t2也會有個 insert intention(2,6)的x鎖在等待gap(2,6)的x鎖的釋放。這樣就形成了死鎖。

         分析到這裡就結束了麼?好像那個地方有點不對。t1本身不就是擁有了一個gap(2,6)的x鎖麼?等等,為什麼在t1擁有gap(2,6)x鎖的情況下,t2還可以擁有gap(2,6)x鎖?x鎖同x鎖不是不相容的麼(看看相容矩陣)?

ix與ix相容,x與x不相容。t1和t2 同時擁有對于表order的ix鎖是可以了解的;但是t1和t2 同時擁有對于表order的index customer_id的x鎖似乎就無法了解了。

唯一錯的地方是官方文檔上面沒有介紹除了這個(is,ix,s,x)的相容矩陣外,在mysql實作内部還有一個更加精确的被稱為“precise mode”的相容矩陣。(該矩陣沒有出現在官方文檔上,是有人通過mysql lock0lock.c:lock_rec_has_to_wait源代碼推測出來的。)

         g    i     r    n (已經存在的鎖,包括等待的鎖)

  g   +     +    +     + 

  i    -      +    +     -

  r   +     +     -     -

  n   +     +     -     -

  + 代表相容, -代表不相容. i代表插入意圖鎖,

  g代表gap鎖,i代表插入意圖鎖,r代表記錄鎖,n代表next-key鎖.

insert語句會對插入的這條記錄加排他記錄鎖,在加記錄鎖之前還會加一種 gap 鎖,叫做插入意向(insert intention)鎖,如果出現唯一鍵沖突,還會加一個共享記錄(s)鎖。 

插入:意向排他鎖,插入意向鎖,如果該間隙已被加上了 gap 鎖或 next-key 鎖,則加鎖失:

死鎖摘錄【yetdone】

s鎖記錄鎖

這裡的表述其實并不準确,有興趣的同學可以去閱讀 innodb 的源碼分析 insert 語句具體的加鎖過程,我在 《讀 mysql 源碼再看 insert 加鎖流程》 這篇部落格中有詳細的介紹。

重點】,該文圓了duplicate primary key的死鎖

死鎖摘錄【yetdone】

插入意向鎖居然是x鎖,s阻塞x,沒毛病

結論:2個經典死鎖案例飙藍加粗黃色底

gap鎖x和s同樣效果,能被n個事務共同進入

gap鎖互相不排斥,但是排斥插入意向鎖

insert時,其加鎖過程為先在插入間隙上擷取插入意向鎖(可能之前還有個意向排鎖),插入資料後再擷取插入行上的排它record鎖。

意向鎖:處理行鎖和表鎖之間的沖突,但插入意向鎖并非意向鎖,而是一種特殊的間隙鎖。

插入意向鎖互不阻塞,能共享,但不代表它是s鎖;像gap x鎖,就能互相共享

record鎖一般是x鎖,除了一種情況,duplicate primary key時會加recode s鎖

s 記錄鎖排斥插入意向x鎖

附加:

1 線上報大量的deadlock 通過指令登入mysql 通過此show engine innodb status\g将列印最近一次死鎖

2 業務層死鎖