MySQL鎖系列(一)之鎖的種類和概念

原文鏈接

https://keithlan.github.io/2017/06/05/innodb_locks_1/

背景


鎖是MySQL裏面最難理解的知識,但是又無處不在。
一開始接觸鎖的時候,感覺被各種鎖類型和名詞弄得暈頭轉向,就別說其他了。
本文是通過DBA的視角(非InnoDB內核開發)來分析和窺探鎖的奧祕,並解決實際工作當中遇到的問題

鎖的種類&概念

想要啃掉這塊最難的大骨頭,必須先畫一個框架,先了解其全貌,才能逐個擊破

  • Shared and Exclusive Locks
* Shared lock: 共享鎖,官方描述:permits the transaction that holds the lock to read a row
 
eg:select * from xx where a=lock in share mode
 
* Exclusive Locks:排他鎖: permits the transaction that holds the lock to update or delete a row
 
eg: select * from xx where a=for update
  • Intention Locks
1. 這個鎖是加在table上的,表示要對下一個層級(記錄)進行加鎖
2. Intention shared (IS):Transaction T intends to set S locks on individual rows in table t
3. Intention exclusive (IX): Transaction T intends to set X locks on those rows
4. 在數據庫層看到的結果是這樣的:
TABLE LOCK table `lc_3`.`a` trx id 133588125 lock mode IX
  • Record Locks
1. 在數據庫層看到的結果是這樣的:
RECORD LOCKS space id 281 page no 3 n bits 72 index PRIMARY of table `lc_3`.`a` trx id 133588125 lock_mode X locks rec but not gap
 
2. 該鎖是加在索引上的(從上面的index PRIMARY of table `lc_3`.`a` 就能看出來)
 
3. 記錄鎖可以有兩種類型:lock_mode X locks rec but not gap && lock_mode S locks rec but not gap
  • Gap Locks
1. 在數據庫層看到的結果是這樣的:
RECORD LOCKS space id 281 page no 5 n bits 72 index idx_c of table `lc_3`.`a` trx id 133588125 lock_mode X locks gap before rec
 
2. Gap鎖是用來防止insert的
 
3. Gap鎖,中文名間隙鎖,鎖住的不是記錄,而是範圍,比如:(negative infinity, 10),(10, 11)區間,這裏都是開區間哦
  • Next-Key Locks
1. 在數據庫層看到的結果是這樣的:
RECORD LOCKS space id 281 page no 5 n bits 72 index idx_c of table `lc_3`.`a` trx id 133588125 lock_mode X
 
2. Next-Key Locks = Gap Locks + Record Locks 的結合, 不僅僅鎖住記錄,還會鎖住間隙,比如: (negative infinity, 10】,(10, 11】區間,這些右邊都是閉區間哦
  • Insert Intention Locks
1. 在數據庫層看到的結果是這樣的:
RECORD LOCKS space id 279 page no 3 n bits 72 index PRIMARY of table `lc_3`.`t1` trx id 133587907 lock_mode X insert intention waiting
 
2. Insert Intention Locks 可以理解爲特殊的Gap鎖的一種,用以提升併發寫入的性能
  • AUTO-INC Locks
1. 在數據庫層看到的結果是這樣的:
TABLE LOCK table xx trx id 7498948 lock mode AUTO-INC waiting
 
2. 屬於表級別的鎖
 
3. 自增鎖的詳細情況可以之前的一篇文章:
http://keithlan.github.io/2017/03/03/auto_increment_lock/
  • 顯示鎖 vs 隱示鎖
* 顯示鎖(explicit lock)
顯示的加鎖,在show engine innoDB status 中能夠看到 ,會在內存中產生對象,佔用內存
eg: select ... for update , select ... lock in share mode
 
* 隱示鎖(implicit lock)
implicit lock 是在索引中對記錄邏輯的加鎖,但是實際上不產生鎖對象,不佔用內存空間
 
* 哪些語句會產生implicit lock 呢?
eg: insert into xx values(xx)
eg: update xx set t=t+where id = 1 ; 會對輔助索引加implicit lock
 
implicit lock 在什麼情況下會轉換成 explicit lock
eg: 只有implicit lock 產生衝突的時候,會自動轉換成explicit lock,這樣做的好處就是降低鎖的開銷
eg: 比如:我插入了一條記錄10,本身這個記錄加上implicit lock,如果這時候有人再去更新這條10的記錄,那麼就會自動轉換成explicit lock
 
* 數據庫怎麼知道implicit lock的存在呢?如何實現鎖的轉化呢?
1. 對於聚集索引上面的記錄,有db_trx_id,如果該事務id在活躍事務列表中,那麼說明還沒有提交,那麼implicit則存在
2. 對於非聚集索引:由於上面沒有事務id,那麼可以通過上面的主鍵id,再通過主鍵id上面的事務id來判斷,不過算法要非常複雜,這裏不做介紹
  • metadata lock
1. 這是Server 層實現的鎖,跟引擎層無關
2. 當你執行select的時候,如果這時候有ddl語句,那麼ddl會被阻塞,因爲select語句擁有metadata lock,防止元數據被改掉
  • 鎖遷移
1. 鎖遷移,又名鎖繼承
2. 什麼是鎖遷移呢?
a) 滿足的場景條件:
b)我鎖住的記錄是一條已經被標記爲刪除的記錄,但是還沒有被puge
c) 然後這條被標記爲刪除的記錄,被purge掉了
d) 那麼上面的鎖自然而然就繼承給了下一條記錄,我們稱之爲鎖遷移
  • 鎖升級
鎖升級指的是:一條全表更新的語句,那麼數據庫就會對所有記錄進行加鎖,那麼可能造成鎖開銷非常大,可能升級爲頁鎖,或者表鎖。
MySQL 沒有鎖升級
  • 鎖分裂
1. InnoDB的實現加鎖,其實是在頁上面做的,沒有辦法直接對記錄加鎖
2. 一個頁被讀取到內存,然後會產生鎖對象,鎖對象裏面會有位圖信息來表示哪些heapno被鎖住,heapno表示的就是堆的序列號,可以認爲就是定位到某一條記錄
3. 大家又知道,由於B+tree的存在,當insert的時候,會產生頁的分裂動作
4. 如果頁分裂了,那麼原來對頁上面的加鎖位圖信息也就變了,爲了保持這種變化和鎖信息,鎖對象也會分裂,由於繼續維護分裂後頁的鎖信息
  • 鎖合併
鎖的合併,和鎖的分裂,其實原理是一樣的,參考上面即可。
 
至於鎖合併和鎖分裂的算法,比較複雜,這裏就不介紹了
  • latch vs lock
* latch
mutex
rw-lock
臨界資源用完釋放
不支持死鎖檢測
以上是應用程序中的鎖,不是數據庫的鎖
 
lock
當事務結束後,釋放
支持死鎖檢測
數據庫中的鎖

鎖的兼容矩陣

  • X vs S
兼容性XS
X N N
S N Y
  • IS,IX,S,X
兼容性ISIXSX
IS Y Y Y N
IX Y Y N N
S Y N Y N
X N N N N
  • AI,IS,IX,S,X
兼容性AIISIXSX
AI N Y Y N N
IS Y Y Y Y N
IX Y Y Y N N
S N Y N Y N
X N N N N N

參考資料

1
2
3
1. https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
2. MySQL技術內幕:InnoDB 存儲引擎
3. MySQL內核:InnoDB 存儲引擎
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章