MySQL鎖分類
- 全局鎖
- 表級鎖
- 行鎖
- 間隙鎖
- next-key lock
全局鎖
-
作用范圍:對整個數(shù)據(jù)庫實例加鎖。(FTWRL)
flush tables with read lock;,該命令會讓整個庫處于只讀狀態(tài),之后其他線程的以下語句會被阻塞:
1、數(shù)據(jù)更新語句(即對數(shù)據(jù)的增刪改操作)。
2、數(shù)據(jù)定義語句(建表,修改表結(jié)構(gòu),加索引等操作)。
3、更新類事務(wù)的提交語句。 - 使用場景:全庫邏輯備份。
-
備份加鎖的必要性
如果不加鎖,備份得到的庫整體不是一個邏輯時間點,視圖是邏輯不一致的。 -
缺點:
1、如果在主庫上備份,整個備份期間都不能執(zhí)行更新,業(yè)務(wù)基本上停擺。
2、如果在從庫上備份,備份期間都不能執(zhí)行從主庫同步過來的binlog,會導致主從延遲。 -
mysqldump優(yōu)點
官方自帶的邏輯備份工具mysqldump,當使用參數(shù)-single-transaction時,導數(shù)據(jù)之前會啟動一個事務(wù),確保拿到一致性視圖。由于MVCC的支持,在這個備份過程中數(shù)據(jù)是可以正常更新的。 -
FTWRL的必要性
mysqldump固然好,但是前提要引擎支持可重復(fù)讀隔離級別,-single-transaction方法只是用于所有的表使用事務(wù)引擎的庫。 -
FTWRL做備份優(yōu)于set global readonly=true
1、某些系統(tǒng)中readonly值可能被用來做其他邏輯,比如判斷一個庫是master還是slave,修改global變量影響大。
2、在異常處理機制上。執(zhí)行FTWRL后若是由于客戶端發(fā)生異常,斷開連接,MySQL會自動釋放這個全局鎖;set global readonly=true方式遇到這種情況會一直保持readonly狀態(tài),會導致整個庫長時間不可寫,風險高。
表級鎖
- 表級鎖包括表鎖和元數(shù)據(jù)鎖。
-
表鎖
lock tables t1 read, t2 write;,此時,其他線程寫t1,讀寫t2的語句都被阻塞。同時,在執(zhí)行unlock tables前,本線程也只能對t1執(zhí)行讀操作,對t2執(zhí)行讀寫操作;寫t1的操作時不允許的,本線程也不能訪問其他表。 - 使用場景:表鎖通常用來處理并發(fā)。對于InnoDB這種支持行鎖的引擎,一般不用表鎖來控制并發(fā)。
-
元數(shù)據(jù)鎖MDL(Meta Data Lock)
MDL不需要顯示調(diào)用,在訪問一個表時會自動加上,作用是保證讀寫的正確性。當對一個表做CRUD操作時,加MDL讀鎖;當對一個表結(jié)構(gòu)變更操作時,加MDL寫鎖。
讀鎖之間不互斥,因此可以多個線程同時對一張表做CRUD操作;寫鎖之間,讀寫鎖之間互斥,用來保證變更表結(jié)構(gòu)操作的安全性,如果有2個線程同時對一個表做結(jié)構(gòu)變更操作,其中一個要等另一個執(zhí)行完才能執(zhí)行。
MDL是系統(tǒng)默認加的,在一個事務(wù)中使用時,在語句執(zhí)行開始時申請,但語句結(jié)束后并不會馬上釋放,要等整個事務(wù)提交后才釋放。所以可能出現(xiàn)給一個小表加字段,導致整個庫掛了的情況,如下:
start transaction; -- sessionA
select * from t limit 1; -- get MDL read and no release
start transaction; -- sessionB
alter table t add f int; -- waiting for session MDL read, blocked
行鎖
- InnoDB行鎖是通過給索引上的索引項加鎖來實現(xiàn)的,意味著:只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖,否則,InnoDB將使用表鎖。
- 行鎖由MySQL各個引擎自己實現(xiàn),并不是所有引擎都支持行鎖,MyISAM就不支持行鎖,InnoDB支持行鎖,這里主要說InnoDB。
- 行鎖分為讀鎖(S鎖,共享鎖)和寫鎖(X鎖,排他鎖),對同一行的操作,只有讀鎖與讀鎖間不沖突。
- 兩階段鎖協(xié)議:在InnoDB中,行鎖是在需要的時候加上,但并不是不需要了就立刻釋放,而是要等到事務(wù)結(jié)束時才釋放。
- 兩階段鎖協(xié)議的影響:如果一個事務(wù)中需要鎖多個行,要把最可能造成鎖沖突,最可能影響并發(fā)度的鎖盡量往后放。同理于MDL鎖的釋放,也就是說一個事務(wù)中的某一行的鎖的持續(xù)時間,根據(jù)兩階段鎖協(xié)議,如果事務(wù)提交的時間點確定,那么越早獲得這一行的行鎖,它的持續(xù)時間越長,所以對并發(fā)度影響大的行要盡量往后放。
- 死鎖:兩個事務(wù)互相等待對方的鎖釋放。
-
死鎖應(yīng)對策略
1、就是等,直到超時;超時時間可通過innodb_lock_wait_timeout設(shè)置,默認50s。(時間設(shè)置太長,很多在線服務(wù)無法接受;時間設(shè)置太短,可能產(chǎn)生很多非死鎖的誤傷)
2、主動發(fā)起死鎖檢測,發(fā)現(xiàn)死鎖后,主動回滾死鎖鏈條中的某一事務(wù),讓其他事務(wù)得以繼續(xù)執(zhí)行;參數(shù)innodb_deadlock_detect=on表示開啟死鎖檢測。 -
主動檢測死鎖的過程:
每當一個事務(wù)要加行鎖的時候,就要看看它所依賴的線程有沒有被別人鎖住(即需要看看自己需要的鎖有沒有在別人手里),如此循環(huán),最后判斷是否出現(xiàn)了循環(huán)等待,也就是死鎖。
間隙鎖
- 鎖住索引樹上相鄰兩個值之間的空隙。
- 間隙鎖使用場景是RR(可重復(fù)讀隔離級別),也就是說它是InnoDB引擎所特有。
next-key lock
- 間隙鎖和行鎖合稱next-key lock。以下是加鎖規(guī)則。
- 原則1:加鎖的基本單位是next-key lock,它是( , ]左開右閉區(qū)間。(除了“唯一索引的等值查詢”,其他“范圍查詢”、“唯一索引范圍查詢”、“非唯一索引的等值查詢”均要找到不滿足條件的“整個區(qū)間”)。
- 原則2:查找過程中訪問到的對象才會加鎖。(若是涉及覆蓋索引的查詢,針對lock in share mode,若是所查詢的字段都包含在覆蓋索引中,那么不會鎖主鍵索引;而for update 和 select 查找索引中沒覆蓋到的字段都會鎖主鍵索引)。
- 優(yōu)化1:唯一索引的等值查詢,當找到符合條件的索引后,索引所在的間隙鎖解除,該next-key lock退化為行鎖。
- 優(yōu)化2:索引上的等值查詢(無論是否是唯一索引),當向右遍歷時且最后一個值不滿足等值條件的時候,next-key lock退化為間隙鎖。
- 補充1:唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。
- 補充2:使用limit可以減小加鎖范圍,取到滿足條件的語句條數(shù),就結(jié)束。delete操作建議加上limit,一是保證安全性,二是減小加鎖范圍。
- 補充3:next-key lock加鎖分為2步,先加間隙鎖,再加行鎖,間隙鎖間不沖突(跟間隙鎖存在沖突關(guān)系的,是“往這個間隙中插入一個記錄”這個操作操作。),行鎖間沖突。這種鎖的順序可能產(chǎn)生死鎖,A拿著B需要的行鎖,B拿著A插入操作需要的間隙鎖)。