MYSQL 我說的那個鎖,不是你的那個鎖

  • 2019 年 10 月 8 日
  • 筆記

一種數據庫中有很多種鎖,一般說起鎖都是在提,是表鎖,還是行鎖,有沒有死鎖。但實際上就算是MYSQL 的鎖的種類也不是那麼簡單。

實際上討論一個鎖,需要從以下幾個方面來考慮

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 這樣的死鎖就不會在存在了。

待……