閱讀203 返回首頁    go iPhone_iPad_Mac_apple


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

背景


鎖是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=1 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=1 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. 自增鎖的詳細情況可以之前的一篇文章:
    https://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+1 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
兼容性 X S
X N N
S N Y
  • IS,IX,S,X
兼容性 IS IX S X
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
兼容性 AI IS IX S X
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. https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
2. MySQL技術內幕:InnoDB 存儲引擎
3. MySQL內核:InnoDB 存儲引擎

最後更新:2017-06-05 16:31:56

  上一篇:go  如果蚊子消失,世界會怎樣?
  下一篇:go  「赫曼方格」視錯覺怎麼破?