PS:本文已收錄到1.3 K+ Star 數的開源項目《大廠面試指北》,如果想要領取《大廠面試指北》離線PDF版,請關注“大廠面試”公衆號領取,或者去點擊入羣二維碼掃碼進羣或者加我微信ruiwendelll,備註獲取資料
項目地址:https://github.com/NotFound9/interviewGuide
入羣二維碼圖片地址:http://notfound9.github.io/interviewGuide/static/49160c2basfdsf.jpeg)
項目截圖:
掃碼領取《大廠面試指北》PDF資料
摘要
這是我總結的一個表格,是本文中涉及到的鎖(因爲篇幅有限就沒有包括自增鎖)
1.數據庫級別的鎖
數據庫級別的鎖有以下兩種:
1.1.全局讀鎖
對數據庫執行Flush tables with read lock
命令讓整個庫處於只讀狀態。
1.2.讓全局只讀
執行set global readonly=true
這個命令也可以讓全庫只能讀,但是第一有些系統會使用readonly來做一個操作,例如根據readonly是否爲true判斷數據庫是否是從庫,第二是如果執行這個命令後,客戶端斷開連接後,數據庫會一直處於只讀狀態,如果是FTWRL命令發送異常會釋放全局鎖。(如果是從庫,設置read-only對super user權限無效)
使用場景:
最常用的場景是對數據庫備份。對數據庫加鎖,讓整個數據庫處於只讀狀態,所有更新操作停止(如果是主庫就不能執行更新語句,從庫也不能執行同步過來的bin log),然後對整個數據庫做邏輯備份(就是將所有數據生成SQL寫入備份文件。)
補充資料:
更好的進行數據庫備份的一種方法
就是通過官方自帶的邏輯備份工具mysqldump來進行邏輯備份時,設置一個參數-single-transaction,這樣導數據的時候就會開啓一個事務,這樣利用innodb的mvcc機制可以保證在事務執行過程中,讀到的數據都跟事務開始時的一致,並且執行過程中,其他事務可以執行更新操作, 不會對他造成影響(因爲它就跟普通SELECT查詢一樣是讀取的快照數據),這種方法必須要求數據庫所有表的引擎都是innodb纔行。
2.表級別的鎖
表級別的鎖有兩種,一種是表鎖,一種是元數據鎖MDL。
2.1表鎖 lock table
就是使用lock table user_table read/write命令來對錶進行加讀鎖或者寫鎖。
-
加讀鎖(也就是表級別共享鎖X鎖)後,表對所有線程都是隻能讀,即便是當前線程也只能讀表,不然會數據不一致。
-
加寫鎖後,表是對當前線程寫,其他線程不能讀,不能回數據不一致。
可以通過unlock tables
來解鎖,客戶端斷開時也會自動釋放鎖,但是影響所有線程,影響面太大了。這種鎖我們一般也不會主動去調用,但是我們去更新一些數據時,如果查詢條件是根據一些沒有索引的字段去查詢的,那樣更新時會主動申請表鎖中的寫鎖,獲取成功後才能修改數據,事務提交成功之後,纔會釋放鎖。(這也是爲什麼我們一般強調對於常用的查詢字段加索引,就是爲了提高更新和讀取效率。)
2.2元數據鎖MDL(MetaData Lock)
分爲讀鎖和寫鎖,加讀鎖時,所有的線程都可以讀表,加寫鎖時,只能一個線程寫,其他的不能讀。
鎖不用顯式使用,是訪問一個表時,自動加上的。
對錶進行增刪改查時,會加讀鎖。
對錶結構做修改時,會加寫鎖。
目的是爲了在增刪改查時不能修改表結構,修改表結構時不能去增刪改查。
2.3 意向鎖
意向鎖的作用主要是表明當前表是否存在數據行加了行鎖。這樣事務可以根據當前表是否有意向鎖來快速判斷當前表是否存在數據行加了行鎖,這樣再加表級別的排斥鎖X,共享鎖S時,避免了去查詢每一行數據,判斷是否加了行鎖,減小了性能開銷。
意向共享鎖(IS鎖)
事務讓一行數據只能讀,需要申請對這行數據加行級別的共享鎖S鎖,在申請行級別的S鎖之前會主動申請表級別的共享意向鎖IS鎖。
意向排斥鎖(IX鎖)
事務在更新某一行數據時,需要申請對這行數據加行級別的排斥鎖X鎖,在申請行級別的X鎖之前會申請表級別的意向鎖IX鎖。
意向鎖之間是兼容的,IS鎖和IX是兼容,因爲可能我們對第一行數據加S鎖,那麼會申請IS鎖,對第二行數據加X鎖,此時跟第一行的數據的S鎖不衝突,所以也會先申請IX鎖,由此可見,IS鎖和IX之間不衝突,IS鎖,IX鎖與行級別的S,行級別的X之間也不衝突。
意向鎖只是跟表級別的S,X鎖可能會衝突。
表級別的S鎖 | 表級別的X鎖 | |
---|---|---|
意向共享鎖IS | 兼容 | 不兼容 |
意向排斥鎖IX | 不兼容 | 不兼容 |
行級別的鎖
行鎖是innodb引擎特有的鎖,也是分爲共享鎖(也就是通常說的讀鎖)和互斥鎖(也就是通常說的寫鎖)
-
共享鎖 S鎖,就是讀鎖,允許事務讀一行數據,不能被修改。所以讀鎖之間不排斥
-
互斥鎖 X鎖,就是寫鎖,就是讓當前事務可以修改這行數據,其他事務不能修改這行數據
如果是從加鎖的範圍來區分,行鎖主要分爲記錄鎖(鎖單個索引),間隙鎖(鎖索引之間的間隙),下一鍵鎖(等於記錄鎖+間隙鎖)
記錄鎖 record lock
記錄鎖鎖定的是單條索引記錄。例如 SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
,如果c是主鍵或者是一個唯一性索引的字段,由於在表內唯一,所以只需要對c=10這個索引進行加鎖,可以防止其他事務插入,更新或刪除這個數據行。
間隙鎖 gap lock
間隙鎖就會對記錄之間的間隙加鎖,防止數據插入。
下一鍵鎖 next-key lock
next-key lock是 record lock 和 gap lock的組合,就是會對索引記錄加記錄鎖 + 索引記錄前面間隙上的鎖”,就是對要更新的數據的左右兩個端點加間隙鎖。
具體案例:
因爲innodb默認的隔離級別是可重複讀,我們在執行更新語句和使用當前讀語句(SELECT…FOR UPDATE)時,都是需要加一些行鎖的,來防止其他事務插入或者刪除數據,導致在事務內多次讀取到的數據行不同。針對行鎖的加鎖規則,極客時間中丁奇老師總結了以下四條規則:
- 原則1:加鎖的基本單位是next-key lock。希望你還記得,next-key lock是前開後閉區間。
- 原則2:查找過程中訪問到的對象纔會加鎖。
- 優化1:索引上的等值查詢,給唯一索引加鎖的時候,next-key lock退化爲記錄鎖。
- 優化2:索引上的等值查詢,向右遍歷時且最後一個值不滿足等值條件的時候,next-key lock退化爲間隙鎖。
- 一個bug:唯一索引上的範圍查詢會訪問到不滿足條件的第一個值爲止。
簡單的來說,我認爲就是next-key lock就是加鎖的基本單位,只不過innodb做了很多優化,在不需要對那麼大範圍的數據行加鎖時,會進行降級,降級爲間隙鎖,或者是記錄鎖。
下面就來看一個具體的例子
例如:
a是一個普通字段,對它建了索引,已有數據是1,5,10,20,30
那麼根據next-key lock來劃分區間,next-key lock是根據已有數據行來劃分區間,並且是左開右閉區間,所以可以鎖定的區間是
(負無窮,1]
(1,5]
(5,10]
(10,20]
(20,30]
(30,正無窮)
//更新操作
update table set b = '1' where a = 10;
在innodb中執行更新操作,
- 如果a是唯一性索引,根據原則3那麼只需要對a爲10的這條索引加記錄鎖就行了,因爲不用擔心其他事務再插入一條a爲10的數據,因爲插入時會有唯一性判斷。
- 但是如果a是非唯一性索引,如果只是對a=10這個索引加鎖,可能會有其他事務插入a=10的數據行,所以會對(5,10]和(10,20]這兩個區間加鎖,並且根據上面的原則4,會將(10,20]降級爲間隙鎖,也就是隻對(10,20)加鎖,因爲a=20這個索引是否加鎖都不影響當前的事務。
- 如果a沒有索引,需要插入時會先申請表級別的互斥鎖X鎖,然後進行插入。
原創不易,最後自薦一下自己做的原創技術公衆號:大廠面試
"大廠面試"這個公衆號是一個幫助程序員進入BAT等互聯網大廠的原創技術號。這裏不僅有面試題分享、大廠內推,還有Java,Python,PHP,前端技術分享,涵蓋多線程、JVM、Spring、MySQL、Redis、微服務等,及阿里,頭條,百度,美團等大廠面試經驗!
- 【大廠面試01期】高併發場景下,如何保證緩存與數據庫一致性?
- 【大廠面試02期】Redis過期key是怎麼樣清理的?
- 【大廠面試03期】MySQL是怎麼解決幻讀問題的?
- 【大廠面試04期】講講一條MySQL更新語句是怎麼執行的?
- 【大廠面試05期】說一說你對MySQL中鎖的理解?
- 【大廠面試06期】談一談你對Redis持久化的理解?
- 【大廠面試07期】說一說你對synchronized鎖的理解?
另外我本人在Github做了一個開源學習指南的項目-《大廠面試指北》,目前在Github已經獲得1.4 K+的Star了
項目地址:https://github.com/NotFound9/interviewGuide
關注公衆號還可以領取《大廠面試指北》PDF版