MySQL間隙鎖與排他鎖的區(qū)別全解析
【MySQL】間隙鎖 與 排他鎖 的區(qū)別
這是一個非常核心的數(shù)據(jù)庫鎖機(jī)制問題。簡單來說:排他鎖(X鎖)是"鎖什么",間隙鎖(Gap Lock)是"鎖哪里"。
?? 核心區(qū)別對比表
| 特性 | 排他鎖 (X鎖) | 間隙鎖 (Gap Lock) |
|---|---|---|
| 鎖的類型 | 鎖類型 - 定義鎖的"權(quán)限" | 鎖范圍 - 定義鎖的"位置" |
| 作用對象 | 已存在的數(shù)據(jù)行 | 數(shù)據(jù)行之間的間隙 |
| 兼容性 | 與任何其他鎖都不兼容 | 與其他間隙鎖兼容,但與插入意向鎖沖突 |
| 主要目的 | 防止其他事務(wù)讀寫數(shù)據(jù) | 防止其他事務(wù)在范圍內(nèi)插入新數(shù)據(jù) |
| 可見范圍 | 行記錄本身 | 索引記錄之間的"空隙" |
| 何時使用 | UPDATE、DELETE、SELECT…FOR UPDATE | 可重復(fù)讀隔離級別下的范圍查詢 |
?? 詳細(xì)解釋
1. 排他鎖(X鎖)- 行級鎖
-- 示例:對uid=8的行加排他鎖 SELECT * FROM student WHERE uid = 8 FOR UPDATE; -- 或 UPDATE student SET age = 60 WHERE uid = 8;
特點(diǎn):
- 鎖定具體的數(shù)據(jù)行
- 其他事務(wù)不能讀?。⊿ELECT…FOR UPDATE)也不能修改(UPDATE/DELETE)該行
- 可以看作"寫鎖"
2. 間隙鎖(Gap Lock)- 范圍鎖
-- 示例:鎖定uid在(5, 10)之間的所有間隙 SELECT * FROM student WHERE uid BETWEEN 6 AND 9 FOR UPDATE; -- 或 SELECT * FROM student WHERE uid > 5 AND uid < 10 FOR UPDATE;
假設(shè)你的表數(shù)據(jù):
uid: 1, 2, 3, 4, 5, 7, 8, 10, 11
間隙包括:(-∞,1), (1,2), (2,3), …, (5,7), (7,8), (8,10), (10,11), (11,+∞)
特點(diǎn):
- 鎖定索引記錄之間的空隙,而不是記錄本身
- 防止其他事務(wù)在范圍內(nèi)插入新數(shù)據(jù)
- 只在可重復(fù)讀(REPEATABLE-READ) 隔離級別下有效
?? 實(shí)際工作場景
場景1:防止幻讀(Phantom Read)
-- 事務(wù)一
BEGIN;
SELECT * FROM student WHERE age BETWEEN 20 AND 30 FOR UPDATE;
-- 鎖定所有age在20-30之間的間隙
-- 事務(wù)二嘗試插入age=25的新記錄(會被阻塞)
INSERT INTO student (name, age, sex) VALUES ('new_student', 25, 'M');場景2:組合使用 - 臨鍵鎖(Next-Key Lock)
MySQL 實(shí)際上經(jīng)常使用 **臨鍵鎖 = 記錄鎖 + 間隙鎖** -- 鎖定uid=8的行及其前后的間隙 SELECT * FROM student WHERE uid = 8 FOR UPDATE; -- 實(shí)際鎖定:(7, 8] 和 (8, 10)
?? 重要注意事項
1. 間隙鎖只作用于非唯一索引
-- 如果name有唯一索引,不會加間隙鎖 SELECT * FROM student WHERE name = 'bbbb' FOR UPDATE; -- 只對'bbbb'這一行加排他鎖 -- 如果name沒有索引或不是唯一索引,可能會鎖全表間隙 SELECT * FROM student WHERE name LIKE 'b%' FOR UPDATE;
2. 查看鎖信息
-- 查看當(dāng)前的鎖(MySQL 8.0+)
SELECT
engine_transaction_id as trx_id,
lock_type,
lock_mode,
lock_data,
index_name
FROM performance_schema.data_locks
WHERE object_name = 'student';3. 死鎖風(fēng)險
間隙鎖容易導(dǎo)致死鎖,特別是并發(fā)插入時:
-- 事務(wù)A BEGIN; SELECT * FROM student WHERE uid = 15 FOR UPDATE; -- 間隙鎖(10, +∞) -- 事務(wù)B BEGIN; SELECT * FROM student WHERE uid = 16 FOR UPDATE; -- 間隙鎖(10, +∞) -- 事務(wù)A INSERT INTO student (uid, name) VALUES (15, 'test'); -- 等待B -- 事務(wù)B INSERT INTO student (uid, name) VALUES (16, 'test'); -- 等待A → 死鎖!
?? 性能影響與優(yōu)化
| 情況 | 影響 | 解決方案 |
|---|---|---|
| 全表間隙鎖 | 鎖住所有間隙,并發(fā)極差 | 使用唯一索引查詢 |
| 范圍查詢 | 鎖住大范圍間隙 | 縮小查詢范圍 |
| 批量操作 | 可能鎖大量間隙 | 分批次操作 |
| 高并發(fā)插入 | 容易死鎖 | 降低隔離級別為RC |
?? 最佳實(shí)踐建議
明確使用場景:
需要防止幻讀 → 使用間隙鎖(RR隔離級別)
只需要防止數(shù)據(jù)修改 → 使用行級排他鎖
索引設(shè)計:
-- 好的設(shè)計:使用唯一索引避免不必要的間隙鎖 CREATE UNIQUE INDEX idx_uid ON student(uid); -- 查詢時使用唯一索引 SELECT * FROM student WHERE uid = 8 FOR UPDATE; -- 只鎖一行
隔離級別選擇:
-- 如果不需要防止幻讀,使用讀已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 此時不會加間隙鎖
避免長事務(wù):
-- 盡快提交事務(wù),減少鎖持有時間 BEGIN; -- 立即執(zhí)行需要加鎖的操作 SELECT ... FOR UPDATE; UPDATE ...; -- 盡快提交 COMMIT;
?? 總結(jié)要點(diǎn)
- 排他鎖是"行鎖" - 鎖已存在的數(shù)據(jù)
- 間隙鎖是"范圍鎖" - 鎖不存在的間隙
- MySQL 默認(rèn)使用 臨鍵鎖(行鎖+間隙鎖)
- 間隙鎖主要在 RR隔離級別 防止幻讀
- 設(shè)計好索引可以大大減少不必要的間隙鎖
在你的學(xué)生表例子中,如果 uid 是唯一索引,SELECT ... WHERE uid=8 FOR UPDATE 只會加行鎖。如果 name 不是唯一索引,相同的查詢可能會加間隙鎖。
到此這篇關(guān)于MySQL間隙鎖與排他鎖的區(qū)別全解析的文章就介紹到這了,更多相關(guān)mysql間隙鎖與排他鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql 5.7.18 安裝配置方法圖文教程(CentOS7)
這篇文章主要為大家詳細(xì)介紹了CentOS 7下mysql 5.7.18 安裝配置方法圖文教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
MySQL8.0數(shù)據(jù)庫開窗函數(shù)圖文詳解
開窗函數(shù)為將要被操作的行的集合定義一個窗口,它對一組值進(jìn)行操作,不需要使用GROUP BY子句對數(shù)據(jù)進(jìn)行分組,能夠在同一行中同時返回基礎(chǔ)行的列和聚合列,這篇文章主要給大家介紹了關(guān)于MySQL8.0數(shù)據(jù)庫開窗函數(shù)的相關(guān)資料,需要的朋友可以參考下2023-06-06
一文帶你了解MySQL之InnoDB統(tǒng)計數(shù)據(jù)是如何收集的
通過show index可以看到關(guān)于索引的統(tǒng)計數(shù)據(jù),那么這些統(tǒng)計數(shù)據(jù)是怎么來的呢,它們是以什么方式收集的呢,本章將聚焦于InnoDB存儲引擎的統(tǒng)計數(shù)據(jù)收集策略,需要的朋友可以參考下2023-05-05
Mysql命令行導(dǎo)入sql數(shù)據(jù)的代碼
Mysql命令行導(dǎo)入sql數(shù)據(jù)的實(shí)現(xiàn)方法是我們經(jīng)常會用到的,下面就為你詳細(xì)介紹Mysql命令行導(dǎo)入sql數(shù)據(jù)的方法步驟,希望對您學(xué)習(xí)Mysql命令行方面能有所幫助。2010-12-12
MySQL?INSERT?導(dǎo)致的死鎖問題分析及解決方案
本文分析MySQL8.4.6中并發(fā)INSERT導(dǎo)致的死鎖,指出因間隙鎖(由外鍵約束和主鍵UUID引發(fā))及頁分裂操作形成循環(huán)等待,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-08-08
MYSQL關(guān)聯(lián)關(guān)系查詢方式
文章詳細(xì)介紹了MySQL中如何使用內(nèi)連接和左外連接進(jìn)行表的關(guān)聯(lián)查詢,并展示了如何選擇列和使用別名,文章還提供了一些關(guān)于查詢優(yōu)化的建議,并鼓勵讀者參考和支持腳本之家2025-02-02
Mysql一對多輕松實(shí)現(xiàn)追蹤歷史首條記錄
本文介紹了在數(shù)據(jù)庫中處理一對多關(guān)系時,如何使用JOIN和WHERE子句來找到每個主表記錄對應(yīng)的子表中特定記錄(例如時間最早的記錄),通過將B表與自身進(jìn)行比較并使用MIN()函數(shù),可以精確匹配到所需記錄2024-12-12
分組查詢GROUP BY的使用與SQL執(zhí)行順序的講解
今天小編就為大家分享一篇關(guān)于分組查詢GROUP BY的使用與SQL執(zhí)行順序的講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03

