在計算機中,鎖是協調多個進程或線程併發訪問某一資源的一種機制。在數據庫當中,數據也是一種供許多用戶共享訪問的資源。如何保證數據併發訪問的一致性、有效性,是全部數據庫必須解決的一個問題,鎖的衝突也是影響數據庫併發訪問性能的一個重要因素。java
MDL
鎖?
MDL
鎖的全稱是Meta Data Lock
,即元數據鎖。它是MySQL
內置級別的鎖,供MySQL
預防共享資源衝突的場景。mysql
MDL
鎖的類型:鎖名稱 | 簡稱 | 鎖類型 | 說明 | 使用語句 |
---|---|---|---|---|
MDL_INTENTION_EXCLUSIVE | S鎖 | 意向鎖,鎖住一個範圍 | 任何語句都會獲取MDL意向鎖,而後再獲取更強級別的MDL鎖。 | |
MDL_SHARED | S | S鎖 | 共享鎖,表示只訪問表結構 | |
MDL_SHARED_HIGH_PRIO | SH | S鎖 | 共享鎖,只訪問表結構 | show create table 等 只訪問INFORMATION_SCHEMA的語句 |
MDL_SHARED_READ | SR | S鎖 | 訪問表結構而且讀表數據 | select語句 LOCK TABLE ... READ |
MDL_SHARED_WRITE | SW | S鎖 | SELECT ... FOR UPDATE DML語句 |
|
MDL_SHARED_UPGRADABLE | SU | S鎖 | 可升級鎖,訪問表結構而且讀寫表數據 | Alter語句中間過程會使用 |
MDL_SHARED_NO_WRITE | SNW | S鎖 | 可升級鎖,訪問表結構而且讀寫表數據,而且禁止其它事務寫。 | Alter語句中間過程會使用 |
MDL_SHARED_NO_READ_WRITE | SNRW | S鎖 | 可升級鎖,訪問表結構而且讀寫表數據,而且禁止其它事務讀寫。 | LOCK TABLES ... WRITE |
MDL_EXCLUSIVE | X | X鎖 | 禁止其它事務讀寫。 | CREATE/DROP/RENAME TABLE等DDL語句。 |
S
鎖表明共享鎖,X
鎖表明排他鎖。sql
MDL
的兼容性矩陣(對象維度)Request type | S | SH | SR | SW | SU | SNW | SNRW | X |
---|---|---|---|---|---|---|---|---|
S | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✘ |
SH | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✘ |
SR | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✘ | ✘ |
SW | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✘ | ✘ | ✘ |
SU | ✔️ | ✔️ | ✔️ | ✔️ | ✘ | ✘ | ✘ | ✘ |
SNW | ✔️ | ✔️ | ✔️ | ✘ | ✘ | ✘ | ✘ | ✘ |
SNRW | ✔️ | ✔️ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
X | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
橫向表示其它事務已經持有的鎖,縱向表示事務想加的鎖。數據庫
屬性 | 含義 | 範圍/對象 |
---|---|---|
GLOBAL | 全局鎖 | 範圍 |
COMMIT | 提交保護鎖 | 範圍 |
SCHEMA | 庫鎖 | 對象 |
TABLE | 表鎖 | 對象 |
FUNCTION | 函數鎖 | 對象 |
PROCEDURE | 存儲過程鎖 | 對象 |
TRIGGER | 觸發器鎖 | 對象 |
EVENT | 事件鎖 | 對象 |
select
語句操做MDL
鎖流程
DML
語句操做MDL
鎖流程
alter
操做MDL
鎖流程
注:
DML
(UPDATE
、INSERT
、DELETE
);DDL
(CREATE
、ALTER
、DROP
);DQL
(SELECT
)。緩存
select
與alter
是否會相互阻塞當執行
select
語句時,只要select
語句在獲取MDL_SHARED_READ
鎖以前,alter
沒有執行到rename階段,那麼select
獲取MDL_SHARED_READ
鎖成功,後續有alter
執行到rename階段,請求MDL_EXCLUSIVE
鎖時,就會被阻塞。bash
DML
與alter
是否會相互阻塞
alter
在opening階段會將鎖升級到MDL_SHARED_NO_WRITE
,rename階段再將升級爲MDL_EXCLUSIVE
,因爲MDL_SHARED_NO_WRITE
與MDL_SHARED_WRITE
互斥,因此先執行alter
或先執行DML
語句,都會致使語句阻塞在opening tables階段。session
select
與DML
是否會相互阻塞因爲
MDL_SHARED_WRITE
與MDL_SHARED_READ
兼容,因此它們不會由於MDL
而致使等待的狀況。併發
flush tables with read lock;
複製代碼
unlock tables
複製代碼
全局鎖的典型使用場景是,作全庫邏輯備份。把整庫每一個表都select出來存成文本。若是不加鎖會不會出現問題呢?函數
表數據變動狀態 | 備份狀態 |
---|---|
account表a用戶有200元餘額account(a,200) course表沒有任何數據 |
|
備份account表 獲得account(a,200) | |
用戶買了一門Java課,花了100元。 | |
account表a用戶有100元餘額 course表數據爲course(a,java) |
|
這個時候我備份跑到course表了,那麼備份結果就是course(a,java) |
獲得最終備份結果即是account(a,200) course(a,java),這顯然是不對的,由於不加鎖的話中間有業務變動,所得數據是不一致。工具
InnoDB
,隔離級別在RR
下,開啓個事務,就能拿到一致性視圖。MySQL
官方提供的備份工具mysqldump
當
mysqldump
使用參數–single-transaction
的時候,導數據以前就會啓動一個事務,來確保拿到一致性視圖。
MySQL
裏面表級別的鎖有兩種:一種是表鎖,一種是MDL
鎖(TABLE
範圍)。
lock tables … read/write
。與 FTWRL 相似,能夠用 unlock tables
主動釋放鎖,也能夠在客戶端斷開的時候自動釋放。MDL
鎖,當屬性爲TABLE,做用範圍爲表級別的時候,它也是一把表鎖。正如咱們上面幾種典型語句的加(釋放)鎖分析的過程當中那樣。它不須要顯示的使用,由於MySQL
會根據你的執行語句來分析是否加鎖和加何種鎖。行鎖的功與過
兩階段鎖協議:在
InnoDB
事務中,行鎖是須要時候才加鎖,但不會不須要了就釋放掉,而是等待事務提交結束時才釋放。
如圖多個用戶都點擊下單的時候,產生鎖競爭的主要場所是影院帳戶餘額新增票價,兩階段鎖協議特性是事務結束才釋放鎖,那麼將這步驟放在最後是鎖暫用時間最短。
事務A | 事務B |
---|---|
update t set k = k+1 where id = 1; | |
update t set k = k+3 where id = 2; | |
update t set k = k+1 where id = 2; | |
update t set k = k+5 where id = 1; |
上面會出現死鎖,解決方式即是按順序加鎖來避免死鎖。
事務A | 事務B |
---|---|
update t set k = k+1 where id = 1; | |
update t set k = k+3 where id = 1; | |
update t set k = k+1 where id = 2; | |
update t set k = k+5 where id = 2; |
對於
insert
,update
,delete
操做,InnoDB
會自動給涉及到的數據加排他鎖,只有select
須要咱們手動設置加鎖級別。
-- 讀鎖(S鎖)
select * from t where id = 1 lock in share mode;
-- 寫鎖(X鎖)
select * from t where id = 1 for update;
複製代碼
InnoDB
的主鍵索引和輔助索引 主鍵索引(聚簇)Record Lock
加鎖策略對於主鍵索引,會在主鍵索引標上鎖標記。對於普通索引,不僅在普通索引標上鎖標記,並且也會在主鍵索引標上。
Gap Lock
加鎖策略間隙鎖它鎖的是索引與索引之間的間隙。
Next-Key Lock
加鎖策略由圖能夠發現
Next-Key Lock
等於Record Lock
加上Gap Lock
。左開右閉。
Next-Key Lock
的須要知道的幾個小事Next-Key Lock
。 原則2:查找過程當中訪問到的對象纔會加鎖。 優化1:索引上的等值查詢,給惟一索引加鎖的時候,Next-Key Lock
退化爲行鎖。 優化2:索引上的等值查詢,向右遍歷時且最後一個值不知足等值條件的時候,Next-Key Lock
退化爲間隙鎖。寫到這,須要明確一個事兒,
Next-Key Lock
是InnoDB RR
隔離級別下的鎖,是內置鎖,是MySQL幫我解決某種場景鎖引入的鎖,那麼這個場景是什麼?其實它想解決的是某種狀況下的幻讀場景。
幻讀僅專指「新插入的行」
id | c | d(key) |
---|---|---|
0 | 0 | 0 |
5 | 5 | 5 |
10 | 10 | 10 |
15 | 15 | 15 |
20 | 20 | 20 |
25 | 25 | 25 |
再看下面這個場景:
session A | sessionB | |
---|---|---|
T1 | begin; update t set d=100 where d=5 |
|
T2 | insert into t values(1,1,5); | |
T3 | commit; |
select * from t where d = 5 for update
複製代碼
由於本想鎖住d=5,這句話的語義被破壞了,「新插入」了一行新的d=5的數據(1,1,5)。
id | c | d(key) |
---|---|---|
0 | 0 | 0 |
5 | 5 | 100 |
10 | 10 | 10 |
15 | 15 | 15 |
20 | 20 | 20 |
25 | 25 | 25 |
1 | 1 | 5 |
咱們再分析下bin log
中的記錄內容
insert into t values(1,1,5);
update t set d=100 where d=5;
複製代碼
能夠發現
bin log
發生與原執行不一樣的結果,出現了數據不一致。爲了解決這個問題,InnoDB RR
級別下,行鎖並不會阻止當前讀狀況下的幻讀問題,才引入了上面所提到的Next-Key Lock
。
寫到這,還須要明確的一件事
Next-Key Lock
只會阻止往當前範圍的insert
動做。並且間隙鎖是內置鎖,InnoDB RR
級別的行鎖默認加的。