MYSQL 我說的那個鎖,不是你的那個鎖
- 2019 年 10 月 8 日
- 筆記
實際上討論一個鎖,需要從以下幾個方面來考慮
1 鎖的種類 表鎖 行鎖
2 加鎖的模式
LOCK_IS
LOCK_IX
LOCK_S
LOCK_X
LOCK_AUTO_INC
3 鎖的類型
4 鎖的粒度
5 鎖所處的隔離級別
NEXT KEY LOCK
LOCK_GAP
LOCK_REC_NOT_GAP
LOCK_INSERT_INTENTION
在知道這些東西後,才能更好的理解鎖及其可能產生的各種死鎖或鎖超時的情況。
下面畫了一個圖,圖中是MYSQL 中提供的鎖的類型從圖中可以看到 IS意向鎖可以和除X鎖的其他鎖類型共存, X 鎖則是和任何鎖都是互斥的,和他本身也是一樣,AI 鎖 只和意向鎖共存。

AUTO_INC 鎖又叫自增鎖(一般簡寫成 AI 鎖),它是一種特殊類型的表鎖,當插入的表中有自增列(AUTO_INCREMENT)的時候可能會遇到。當插入表中有自增列時,數據庫需要自動生成自增值,在生成之前,它會先為該表加 AUTO_INC 表鎖,其他事務的插入操作阻塞,這樣保證生成的自增值肯定是唯一的。
- AUTO_INC 鎖互不兼容,同一張表一個時刻只能有一個自增鎖
- 自增鎖不遵循二段鎖協議,不是事務over時release,在 INSERT 語句執行完成時釋放,用以提高並發插入的性能。
- 自增值一旦分配了就會 +1,如果事務回滾,自增值也不會減回去,所以自增值可能會出現中斷的情況。
而我們熟悉的行鎖
LOCK_REC_NOT_GAP ,record 鎖本身是沒有那麼複雜的,他僅僅對他所在的記錄進行一個鎖,而相關的鎖,僅僅是鎖在索引上邊的,如果是primary key 則直接鎖在主鍵的位置,如果是二級索引,則除了鎖在二級索引上,同時還需要鎖在二級索引所指定的主鍵上。
NEXT KEY LOCK next key lock,顧名思義 要不是 ( ] [ ) ,(一個集合的概念),他主要的作用是防止幻讀,也就是兩次讀不一致的情況,所以LOCK_GAP 主要是要看所處的
隔離級別是R R , RC 那兩種,MYSQL 默認的隔離級別是 RR ,但一般來說強烈建議 MYSQL 的通用的使用的隔離級別是 RC 。如果我們的隔離級別是RC 級別的情況下是不會有 next key lock 這樣的鎖。NEXT KEY LOCK 鎖會將鎖定記錄周圍的記錄也進行一個鎖定。
舉例:如果我們的數據表中 的數據記錄是 1 6 7 8 9 10
select * from t where number = 6 for update
此時鎖定的記錄
(1 6 7),此時如果在 1和 6之間插入數據會無法插入
GAP LOCK 間隙鎖,間隙鎖的知名度比 NEXT KEY LOCK 要大的的
(),標識間隙鎖,間隙鎖本來也可以理解成為範圍鎖,他將防止其他事務在這個範圍內插入或修改記錄,保證兩次讀取這個範圍內的記錄不會變,從而不會出現幻讀現象。添加間隙鎖和間隙鎖之間是不衝突的,而添加間隙鎖會嚴重影響數據庫的並發性,還以上面的例子來說,他是要鎖定 1(23456)7 ,同時不同的事務可以在間隙上持有衝突鎖。例如,事務A可以在一個gap上持有一個共享的gap鎖(gap S-lock),而事務B在同一個gap上持有一個獨佔的gap鎖(gap X-lock)。允許存在衝突的間隙鎖的原因是,如果從索引中清除一條記錄,則必須合併不同事務在記錄中持有的間隙鎖。
LOCK_INSERT_INTENTION 插入意向鎖,主要是服務於插入服務的,在數據庫插入的時候會診斷插入數據的位置是否有間隙鎖,也就是和間隙鎖next key lock 這樣的鎖互斥。
記錄鎖和記錄鎖衝突,Next-key 鎖和 Next-key 鎖衝突,記錄鎖和 Next-key 鎖衝突;
舉例我們現在有下面一張表

我們模擬兩個SESSION
1 select * from insert_lock where id <= 5 for update
2 insert into insert_lock (id,name,employee_number) values (4,'rty',12)

上邊的圖中可以清晰的看到 select 的查詢中包含了 GAP 鎖,所以GAP 鎖導致 插入失敗。
那如何避免上面的情況 ,直接將數據庫的隔離級別從 RR 改為 RC 這樣的死鎖就不會在存在了。
待……