MySQL?中的行鎖(Record?Lock)?和?間隙鎖(Gap?Lock)詳解
1. 行鎖(Record Lock)
定義
- Record Lock 是 InnoDB 在事務(wù)中對(duì)索引記錄加的鎖,用于保護(hù)某一行數(shù)據(jù)不被其他事務(wù)修改。
- 它是基于索引的鎖,如果沒有索引,InnoDB 會(huì)退化為表鎖。
作用
- 防止其他事務(wù)修改或刪除當(dāng)前事務(wù)正在處理的行。
- 保證事務(wù)的隔離性(尤其是
REPEATABLE READ和SERIALIZABLE隔離級(jí)別)。
觸發(fā)場(chǎng)景
- 常見于
SELECT ... FOR UPDATE或UPDATE、DELETE操作。 - 必須通過索引定位行,否則會(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)插入新記錄。
- 常見于范圍查詢(
BETWEEN、>、<)或REPEATABLE READ隔離級(jí)別下的SELECT ... FOR UPDATE。
觸發(fā)場(chǎng)景
- 范圍查詢并加鎖時(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é)
| 鎖類型 | 鎖住對(duì)象 | 作用 | 常見場(chǎng)景 |
|---|---|---|---|
| Record Lock | 某一行的索引記錄 | 防止該行被修改或刪除 | 精確匹配索引的 UPDATE、DELETE、SELECT ... FOR UPDATE |
| Gap Lock | 索引記錄之間的空隙 | 防止在范圍內(nèi)插入新記錄 | 范圍查詢加鎖,防止幻讀 |
? 總結(jié):
- Record Lock:鎖住已有的行,防止修改或刪除。
- Gap Lock:鎖住索引間的空隙,防止插入新行。
- 兩者經(jīng)常一起出現(xià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沒有索引,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 之間范圍特別大,可以考慮:
- 拆分查詢,減少一次性鎖定的范圍
- 使用更精確的條件,避免鎖住無(wú)關(guān)數(shù)據(jù)
- 降低隔離級(jí)別(如
READ COMMITTED)減少 Gap Lock
為什么5前面沒有被鎖?。?/strong>
在執(zhí)行
BEGIN; SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;
的時(shí)候,為什么 id=5 前面的間隙(比如 (1,5))沒有被鎖住?
核心原因:鎖定范圍是由查詢條件決定的
InnoDB 的 Next-Key Lock(行鎖 + 間隙鎖)只會(huì)鎖住滿足查詢條件的記錄以及這些記錄之間的間隙。
- 查詢條件是
id BETWEEN 5 AND 50 - 范圍的起點(diǎn)是
id=5 - 所以它不會(huì)去鎖
(1,5)這個(gè)間隙,因?yàn)檫@個(gè)間隙里的值(比如 2、3、4)不滿足條件,不會(huì)影響當(dāng)前事務(wù)的結(jié)果集。
鎖定范圍的精確規(guī)則
在 REPEATABLE READ 隔離級(jí)別下:
- Record Lock:鎖住滿足條件的記錄(這里是
id=5, 10, 20, 50)。 - Gap Lock:鎖住這些記錄之間的間隙,以及最后一條記錄到下一條記錄之間的間隙。
- 不鎖條件范圍外的間隙,因?yàn)樗鼈儾粫?huì)導(dǎo)致幻讀。
例子對(duì)比
假設(shè)表數(shù)據(jù):
id: 1, 5, 10, 20, 50, 100
查詢:
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)文章
Win10下免安裝版MySQL8.0.16的安裝和配置教程圖解
這篇文章主要介紹了Win10下免安裝版MySQL8.0.16的安裝和配置 ,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考解決價(jià)值,需要的朋友可以參考下2019-06-06
MySQL Semisynchronous Replication介紹
這篇文章主要介紹了MySQL Semisynchronous Replication介紹,本文講解了Semisynchronous Replication 定義、,需要的朋友可以參考下2015-05-05
MySQL數(shù)據(jù)庫(kù)查詢性能優(yōu)化策略
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)查詢性能優(yōu)化的策略,幫助大家的工作學(xué)習(xí)提高M(jìn)ySQL數(shù)據(jù)庫(kù)的性能,感興趣的朋友可以了解下2020-08-08
mysql如何分別按年/月/日/周分組統(tǒng)計(jì)數(shù)據(jù)詳解
我們?cè)谟肕ysql抽取數(shù)據(jù)時(shí)候,經(jīng)常需要按照天、周、月等不同的粒度對(duì)數(shù)據(jù)進(jìn)行分組統(tǒng)計(jì),下面這篇文章主要給大家介紹了關(guān)于mysql如何分別按年/月/日/周分組統(tǒng)計(jì)數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2022-12-12
MySQL中SHOW TABLE STATUS的使用及說(shuō)明
這篇文章主要介紹了MySQL中SHOW TABLE STATUS的使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10

