MySQL?中的行鎖(Record?Lock)?和?間隙鎖(Gap?Lock)詳解
1. 行鎖(Record Lock)
定義
- Record Lock 是 InnoDB 在事務(wù)中對(duì)索引記錄加的鎖,用于保護(hù)某一行數(shù)據(jù)不被其他事務(wù)修改。
- 它是基于索引的鎖,如果沒(méi)有索引,InnoDB 會(huì)退化為表鎖。
作用
- 防止其他事務(wù)修改或刪除當(dāng)前事務(wù)正在處理的行。
- 保證事務(wù)的隔離性(尤其是
REPEATABLE READ和SERIALIZABLE隔離級(jí)別)。
觸發(fā)場(chǎng)景
- 常見(jiàn)于
SELECT ... FOR UPDATE或UPDATE、DELETE操作。 - 必須通過(guò)索引定位行,否則會(huì)鎖住更多數(shù)據(jù)(甚至全表)。
例子
假設(shè)有表:
CREATE TABLE user ( id INT PRIMARY KEY, name VARCHAR(50), age INT ) ENGINE=InnoDB;
事務(wù) A:
BEGIN; SELECT * FROM user WHERE id=5 FOR UPDATE;
- InnoDB 會(huì)在
id=5這一行的索引記錄上加 Record Lock。 - 事務(wù) B 如果執(zhí)行:
UPDATE user SET age=30 WHERE id=5;
會(huì)被阻塞,直到事務(wù) A 提交或回滾。
2. 間隙鎖(Gap Lock)
定義
- Gap Lock 是 InnoDB 在事務(wù)中對(duì)索引記錄之間的間隙加的鎖。
- 它鎖住的是索引之間的空隙,而不是具體的行。
- 主要用于防止幻讀(Phantom Read)。
作用
- 阻止其他事務(wù)在某個(gè)索引范圍內(nèi)插入新記錄。
- 常見(jiàn)于范圍查詢(xún)(
BETWEEN、>、<)或REPEATABLE READ隔離級(jí)別下的SELECT ... FOR UPDATE。
觸發(fā)場(chǎng)景
- 范圍查詢(xún)并加鎖時(shí)(
FOR UPDATE或LOCK IN SHARE MODE)。 - 在
REPEATABLE READ下,InnoDB 會(huì)自動(dòng)加 Gap Lock 來(lái)防止幻讀。
例子
假設(shè)表中有數(shù)據(jù):
id: 1, 5, 10
事務(wù) A:
BEGIN; SELECT * FROM user WHERE id BETWEEN 5 AND 10 FOR UPDATE;
- InnoDB 會(huì)對(duì)
id=5和id=10加 Record Lock。 - 同時(shí)會(huì)對(duì)
(5,10)之間的空隙加 Gap Lock。 - 事務(wù) B 如果執(zhí)行:
INSERT INTO user VALUES (7, 'Tom', 25);
會(huì)被阻塞,因?yàn)?(5,10) 的間隙被鎖住。
3. 區(qū)別總結(jié)
| 鎖類(lèi)型 | 鎖住對(duì)象 | 作用 | 常見(jiàn)場(chǎng)景 |
|---|---|---|---|
| Record Lock | 某一行的索引記錄 | 防止該行被修改或刪除 | 精確匹配索引的 UPDATE、DELETE、SELECT ... FOR UPDATE |
| Gap Lock | 索引記錄之間的空隙 | 防止在范圍內(nèi)插入新記錄 | 范圍查詢(xún)加鎖,防止幻讀 |
? 總結(jié):
- Record Lock:鎖住已有的行,防止修改或刪除。
- Gap Lock:鎖住索引間的空隙,防止插入新行。
- 兩者經(jīng)常一起出現(xiàn),比如范圍查詢(xún)時(shí)會(huì)同時(shí)鎖住已有行和間隙,這種組合鎖叫 Next-Key Lock。
BEGIN; SELECT * FROM user WHERE id BETWEEN a AND b FOR UPDATE;
如果a和b距離很大是否直接鎖住a和b中間所有行
是的,
在 InnoDB 的 REPEATABLE READ 隔離級(jí)別下,執(zhí)行
BEGIN; SELECT * FROM user WHERE id BETWEEN a AND b FOR UPDATE;
會(huì)觸發(fā) Next-Key Lock(Record Lock + Gap Lock 的組合),它會(huì)鎖住范圍內(nèi)所有匹配的行,并且鎖住這些行之間的間隙,防止其他事務(wù)在這個(gè)范圍內(nèi)插入新數(shù)據(jù)。
1. 鎖定范圍的規(guī)則
假設(shè) id 是主鍵索引,表中數(shù)據(jù)如下:
id: 1, 5, 10, 20, 50, 100
如果執(zhí)行:
SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;
InnoDB 會(huì):
- 對(duì)
id=5、id=10、id=20、id=50加 Record Lock(行鎖) - 對(duì)
(5,10)、(10,20)、(20,50)這些間隙加 Gap Lock - 還會(huì)對(duì)
(50, 下一條記錄)的間隙加鎖(防止插入 51、60 等)
2. 如果 a 和 b 很遠(yuǎn)
如果 a=5,b=1000000,而表中有很多行:
- 是的,InnoDB 會(huì)鎖住
id=5到id=1000000范圍內(nèi)所有匹配的行和它們之間的間隙。 - 這意味著這個(gè)范圍內(nèi)的更新、刪除、插入都會(huì)被阻塞,直到事務(wù)提交或回滾。
- 如果范圍特別大,可能會(huì)造成大范圍鎖定,嚴(yán)重影響并發(fā)性能。
3. 注意事項(xiàng)
- 基于索引:行鎖和間隙鎖都是基于索引的,如果
id沒(méi)有索引,InnoDB 會(huì)退化成表鎖,鎖住整張表。 - 范圍越大,鎖的行和間隙越多,并發(fā)沖突風(fēng)險(xiǎn)越高。
- 如果只想鎖住已存在的行而不鎖間隙,可以使用
READ COMMITTED隔離級(jí)別(Gap Lock 不會(huì)生效)。
4. 直觀示意
假設(shè):
id: 1, 5, 10, 20, 50, 100
執(zhí)行:
SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;
鎖定范圍:
[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====
- [x] 表示 Record Lock(行鎖)
- (gap) 表示 Gap Lock(間隙鎖)
我建議,如果你的 a 和 b 之間范圍特別大,可以考慮:
- 拆分查詢(xún),減少一次性鎖定的范圍
- 使用更精確的條件,避免鎖住無(wú)關(guān)數(shù)據(jù)
- 降低隔離級(jí)別(如
READ COMMITTED)減少 Gap Lock
為什么5前面沒(méi)有被鎖???
在執(zhí)行
BEGIN; SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;
的時(shí)候,為什么 id=5 前面的間隙(比如 (1,5))沒(méi)有被鎖住?
核心原因:鎖定范圍是由查詢(xún)條件決定的
InnoDB 的 Next-Key Lock(行鎖 + 間隙鎖)只會(huì)鎖住滿(mǎn)足查詢(xún)條件的記錄以及這些記錄之間的間隙。
- 查詢(xún)條件是
id BETWEEN 5 AND 50 - 范圍的起點(diǎn)是
id=5 - 所以它不會(huì)去鎖
(1,5)這個(gè)間隙,因?yàn)檫@個(gè)間隙里的值(比如 2、3、4)不滿(mǎn)足條件,不會(huì)影響當(dāng)前事務(wù)的結(jié)果集。
鎖定范圍的精確規(guī)則
在 REPEATABLE READ 隔離級(jí)別下:
- Record Lock:鎖住滿(mǎn)足條件的記錄(這里是
id=5, 10, 20, 50)。 - Gap Lock:鎖住這些記錄之間的間隙,以及最后一條記錄到下一條記錄之間的間隙。
- 不鎖條件范圍外的間隙,因?yàn)樗鼈儾粫?huì)導(dǎo)致幻讀。
例子對(duì)比
假設(shè)表數(shù)據(jù):
id: 1, 5, 10, 20, 50, 100
查詢(xún):
SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;
鎖定范圍:
[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====
[x]= Record Lock(gap)= Gap Lock(1,5)不鎖,因?yàn)椴辉跅l件范圍內(nèi)。
如果是:
SELECT * FROM user WHERE id >= 5 FOR UPDATE;
鎖定范圍:
[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====[100]====(gap)====
- 依然不會(huì)鎖
(1,5),因?yàn)闂l件是>=5。
如果是:
SELECT * FROM user WHERE id > 1 FOR UPDATE;
鎖定范圍:
(1,5)====[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====[100]====(gap)====
- 這時(shí)
(1,5)會(huì)被鎖,因?yàn)樗跅l件范圍內(nèi)(>1)。
? 總結(jié):
- InnoDB 只鎖條件范圍內(nèi)的記錄和間隙,不會(huì)鎖條件范圍外的間隙。
BETWEEN 5 AND 50的起點(diǎn)是 5,所以 5 前面的間隙不會(huì)被鎖。- 如果條件包含更小的值(比如
>1),那么 5 前面的間隙就會(huì)被鎖。
到此這篇關(guān)于MySQL 中的行鎖(Record Lock) 和 間隙鎖(Gap Lock)詳解的文章就介紹到這了,更多相關(guān)mysql行鎖和間隙鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL數(shù)據(jù)庫(kù)必備之條件查詢(xún)語(yǔ)句
當(dāng)用戶(hù)查看表格的大量數(shù)據(jù)是,由于數(shù)據(jù)量過(guò)于巨大會(huì)導(dǎo)致很難獲取到需要的數(shù)據(jù),在這時(shí),就需要一個(gè)方法,一個(gè)可以通過(guò)用戶(hù)輸入獲取到用戶(hù)需要的數(shù)據(jù)并回填入表格,這就是條件查詢(xún)的作用2021-10-10
解決阿里云ECS服務(wù)器下安裝MySQL無(wú)法遠(yuǎn)程連接的問(wèn)題
這篇文章介紹了解決阿里云ECS服務(wù)器安裝MySQL無(wú)法遠(yuǎn)程連接的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
MySQL MHA 運(yùn)行狀態(tài)監(jiān)控介紹
這篇文章主要介紹MySQL MHA 運(yùn)行狀態(tài)監(jiān)控,MHA(Master HA)是一款開(kāi)源的 MySQL 的高可用程序,它為 MySQL 主從復(fù)制架構(gòu)提供了 automating master failover 功能,想具體了解的小伙伴可以和小編一起學(xué)習(xí)下面文章內(nèi)容2021-10-10
幾種MySQL中的聯(lián)接查詢(xún)操作方法總結(jié)
這篇文章主要介紹了幾種MySQL中的聯(lián)接查詢(xún)操作方法總結(jié),文中包括一些代碼舉例講解,需要的朋友可以參考下2015-04-04
庫(kù)名表名大小寫(xiě)問(wèn)題與sqlserver兼容的啟動(dòng)配置方法
庫(kù)名表名大小寫(xiě)問(wèn)題與sqlserver兼容的啟動(dòng)配置方法,需要的朋友可以參考下。2010-12-12
MySQL數(shù)據(jù)庫(kù)的觸發(fā)器的使用
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)的觸發(fā)器的使用,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-09-09
如何使用mysqladmin獲取一個(gè)mysql實(shí)例當(dāng)前的TPS和QPS
這篇文章主要介紹了如何使用mysqladmin這個(gè)工具來(lái)獲取一個(gè)mysql實(shí)例當(dāng)前的TPS和QPS,幫助大家更好的管理數(shù)據(jù)庫(kù),感興趣的朋友可以了解下2020-11-11
數(shù)據(jù)庫(kù)的用戶(hù)帳號(hào)管理基礎(chǔ)知識(shí)
數(shù)據(jù)庫(kù)的用戶(hù)帳號(hào)管理基礎(chǔ)知識(shí)...2006-11-11

