什麼是 MySQL 鎖?有哪些鎖類型?
爲什麼需要引入鎖
鎖是計算機協調多個進程或線程併發訪問某一資源的一種機制,在併發事務下保證數據的正確和唯一性。
鎖在 MySQL 中是非常重要的一部分,對 MySQL 的數據訪問併發有着舉足輕重的影響
MySQL 中的鎖是在服務器層或存儲引擎層實現的,不同的存儲引擎的鎖機制也有較大的區別。
MySQL 鎖的實現
很多人都一樣,在剛開始學習 MySQL 中鎖的時候,網上一查出來一堆,什麼表鎖、行鎖、讀鎖、寫鎖、悲觀鎖、樂觀鎖等等等,直接整個人就懵了。
本文我們將以鎖粒度的角度去看 MySQL 鎖的分類情況
沒事,先看看小許歸納的鎖知識大綱,先對鎖的位置和鎖歸屬的存儲引擎有個前置瞭解!
全局鎖
全局鎖就是對整個數據庫實例加鎖,MySQL 有個全局讀鎖的命令如下:
flush tables with read lock(FTWRL)
執行後,整個數據庫就處於只讀狀態(不能寫入) 了,這個時候其他線程執行數據更新語句(數據的增刪改),數據定義語句(建表、修改表結構等)等,都會被阻塞。
解鎖命令:
unlock tables
使用場景舉例:
主要應用於做全庫邏輯備份,原理也很簡單在全局鎖期間數據或表結構不會被更新,備份後文件的數據與預期也就一樣了。
當時加上全局鎖,意味着整個數據庫都是隻讀狀態,如果備份時間過長就導致其他
Mysql 中數據備份使用的命令是 mysqldump 命令
當使用參數 - single-transaction 的時候,導出數據之前就會啓動一個事務,來確保拿到一致性視圖,而由於 MVCC 的支持,這個過程中數據是可以正常更新的,因爲讀取的數據在更新前已確認。
頁鎖
頁級鎖是 MySQL 中比較獨特的一種鎖定級別,主要應用於 BDB 存儲引擎,我們實際中基本上用的是 InnoDB 引擎,這裏對頁鎖就不多展開了。
表鎖
MyISAM 和 InnoDB 都支持表級鎖,但是 InnoDB 默認的是行級鎖。
表鎖下面又分了以下四種
表鎖
顧名思義,就是直接對錶進行加鎖,可以使用下面命令:
//加讀鎖
lock tables table_name read;
//加寫鎖
lock tables table_name write;
// 釋放當前會話的所有表鎖
unlock tables
如果加的是寫鎖,當對錶進行寫操作時也會被阻塞,直到寫鎖被釋放。
不過儘量避免在使用 InnoDB 引擎的表使用表鎖,因爲表鎖的顆粒度太大,會影響併發性能。
元數據鎖
MySQL5.5 引入了元數據鎖(meta data lock - MDL),它不需要顯式使用,在訪問一個表的時候會被自動加上。
-
• 對錶數據進行 CRUD 操作時,加 MDL 讀鎖
-
• 對錶結構變更操作的時候,加 MDL 寫鎖
既然是自動加鎖,那釋放也是自動的!
事務執行期間,MDL 是一直持有的, 在事務提交後 MDL 纔會釋放。
意向鎖(Intention Lock)
意向鎖主要是在對數據表的行記錄加共享鎖 (S 鎖)、獨佔鎖(X 鎖) 之前,需要先在表級別加上一個意向鎖。
在 InnoDB 引擎中,當執行查詢操作,需要先對錶加上「意向共享鎖」,然後對該記錄加【共享鎖】
意向鎖有兩種類型:
意向共享鎖(IS 鎖):一個事務給一個數據行加共享鎖時,必須先獲得表的意向共享鎖
意向獨佔鎖(IX 鎖): 一個事務給一個數據行加獨佔鎖時,必須先獲得表的意向獨佔鎖
爲什麼需要先加意向鎖?
意向鎖的目的是更加快速的判斷數據表表裏是否有記錄被加鎖。
比如我們要加【獨佔表鎖】,先在表級別加了【意向獨佔鎖】,那麼在加【獨佔鎖】時,直接查該表是否有意向獨佔鎖,如果有就表示表記錄存在獨佔鎖,這樣就不用去遍歷表記錄去查看行記錄是否存在獨佔鎖了。
加鎖命令如下:
//加上意向共享鎖,然後對讀取的記錄加共享鎖
select ... lock in share mode;
//先表上加上意向獨佔鎖,然後對讀取的記錄加獨佔鎖
select ... for update;
AUTO-INC 鎖
字面意思是用來控制自動自增的鎖?
是的,一般來說我們會在表中設置一個字段聲明 AUTO_INCREMENT 的自增 ID 字段。
AUTO-INC 鎖在自增字段起了個什麼作用呢?
當使用 INSERT 語句插入一條新記錄時,MySQL 會自動爲自增字段加鎖,防止其他併發的插入操作同時獲取相同的自增值。
其他事務要等待,直到執行完插入語句之後纔會釋放鎖。
這就保證了數據表的 AUTO_INCREMENT 字段的值是連續遞增。
好吧,原來這個 AUTO_INC 鎖的作用是這樣的,以前我還一直不知道呢!
👉 AUTO-INC 鎖有什麼問題?
大批量數據在一條語句中插入時 (INSERT SELECT),會帶來一些性能上的影響,從而阻塞其他事務的插入操作!
🚩 MySQL 是如何進行 AUTO-INC 鎖性能優化的?
MYSQL 5.1.22 版本開始,InnoDB 存儲引擎使用一種輕量級互斥鎖 (Mutex) 來控制自增列增長
通過參數 innodb_autoinc_lock_mode 來控制 可以設定 3 個值分別是 0,1,2
-
• 0:traditional 每次 insert 都採用 AUTO-INC 鎖,語句執行結束後才釋放鎖,但併發能力較弱
-
• 1:consecutive 對於 SIMPLE INSERT,使用輕量級互斥鎖,對於 BULK INSERT,使用 AUTO-inc locking
-
• 2:interleaved 採用輕量級鎖,申請自增主鍵後就釋放鎖,但可能會造成 insert 分配的 id 順序不一致
🚩 一個事務中存在多個 insert 語句,auto-inc 鎖是如何申請的?
自增鎖跟事務無關,即使多個 insert 語句在同一個十五中,每個 insert 還是都會申請罪行的自增鎖。
行鎖
顧名思義,行鎖就是給數據庫表中每行數據加鎖,行鎖是加在索引上的
比如某個表中 id 字段是主鍵,如果給 id=2 這條記錄加鎖,那這把鎖是加在主鍵索引 (聚簇索引) 上的
行鎖使用分類
我們講表鎖的時候說到了意向鎖,在對數據表的行記錄加共享鎖 (S 鎖)、獨佔鎖(X 鎖) 之前,需要先在表級別加上一個意向鎖 。
InnoDB 行級鎖按照使用方式分爲:共享鎖(S 鎖)、排它鎖(X 鎖)
讀鎖會阻塞寫 (X),但是不會堵塞讀(S),而寫鎖則會把讀(S) 和寫 (X) 都堵塞
對於普通 select 語句,innodb 不會加任何鎖。如果想在 select 操作的時候加上 S 鎖 或者 X 鎖,需要我們手動加鎖。
//查詢記錄加共享鎖
select ... lock in share mode;
//查詢記錄加獨佔鎖
select ... for update;
InnoDB 在 RR(MySQL 默認隔離級別) ,對於 update、delete 和 insert 語句, 會自動給涉及的數據集加排它鎖(X)
InnoDB 支持 3 種行鎖的算法,分別是:
-
• Record Lock: 單個行記錄上的鎖
-
• Gap Lock: 間隙鎖,鎖定一個範圍,但不包含記錄本身
-
• Next-Key Lock: Gap Lock 與 Record Lock 的結合,鎖定一個範圍,並且鎖定記錄本身
我們在分析行鎖三種算法是要結合存在共享鎖(S)和排他鎖(X)場景,我們接着看這三種
記錄鎖 Record Lock
Record Lock 稱爲記錄鎖,鎖住的是一條記錄
SELECT * FROM `demo` WHERE `id`= 23 FOR UPDATE;
上面 SQL 在 id = 23 的記錄上加上記錄鎖(X 鎖),這樣其他事務就無法插入,更新,刪除 id=23 這一行。
下面 SQL 是對主鍵索引 與 唯一索引 對數據行進行 UPDATE 操作時,也會對該行數據加記錄鎖:
UPDATE demo SET name = 'xiaoxu' WHERE id = 23;
記錄鎖是鎖住記錄,鎖住索引記錄,而不是真正的數據記錄。
🚩 表中沒有建索引怎麼辦?
即使該表上沒有任何索引,那麼 innodb 會在後臺創建一個隱藏的聚集主鍵索引,那麼鎖住的就是這個隱藏的聚集主鍵索引。
間隙鎖 GAP Lock
間隙鎖 是 InnoDB 在 RR(可重複讀) 隔離級別 下爲了解決幻讀問題時引入的鎖機制。
Tips:使用間隙鎖 GAP Lock 鎖住的是一個區間,而不僅僅是這個區間中的每一條數據
SELECT * FROM demo WHERE id > 23 and id < 25 FOR UPDATE
上面語句對 id 範圍(23, 25)的數據行加間隙鎖鎖,此時就無法插入 id= 24 的數據
臨鍵鎖 Next-Key Lock
Next-key Lock 臨鍵鎖是記錄鎖和間隙鎖的組合,鎖的範圍是左開右閉區間的數據(即在某條記錄以及這條記錄前面間隙上的鎖)。
InnoDB 是使用 Next-Key Lock 來解決幻讀問題的,在數據行上的非唯一索引列上都會存在一把臨鍵鎖。
注意:臨鍵鎖只與 非唯一索引列 有關,在 唯一索引列(包括主鍵列)上不存在臨鍵鎖。
上面表結構中 age 字段爲普通索引
-- 事務A 更新age=24的記錄
UPDATE demo SET name = Vladimir WHERE age = 24;
-- 事務B 執行插入
INSERT INTO demo VALUES(100, 26, 'xiaoxu');
事務 A 在對 age 爲 24 的列進行 UPDATE 操作的同時,也獲取了 (24, 26] 這個區間內的臨鍵鎖,所以此時事務 B 會被阻塞。
問題
臨鍵鎖 Next-Key Lock 如何降級?
細心的朋友會發現開頭的題綱中有一個降級的指向,那麼是在什麼情況下發生降級的呢?
在能使用記錄鎖或者間隙鎖就能避免幻讀現象的場景下, next-key lock 就會退化成記錄鎖或間隙鎖。
有以下場景:
唯一索引等值查詢:
- 當查詢的記錄是存在的,next-key lock 會退化成【記錄鎖】 2. 當查詢的記錄是不存在的,next-key lock 會退化成【間隙鎖】
非唯一索引等值查詢:
- 當查詢的記錄存在時,除了會加 next-key lock 外,還額外加間隙鎖,也就是會加兩把鎖。
- 當查詢的記錄不存在時,只會加 next-key lock,然後會退化爲間隙鎖,也就是隻會加一把鎖。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/gAJFm3q5510PfRBe4F11PQ