最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深度解析MySQL 鎖機制:間隙鎖、Next-Key Lock 與幻讀防御

 更新時間:2026年03月03日 09:59:47   作者:知識即是力量ol  
本文深入探討了MySQL的鎖機制,特別是間隙鎖(GapLock)和臨鍵鎖(Next-KeyLock),本文將從"為什么要鎖間隙"出發(fā),一路追到"為什么是左開右閉",把這套機制徹底講透,感興趣的朋友跟隨小編一起看看吧

導語: 當你在 MySQL 中執(zhí)行一條 UPDATE、DELETESELECT ... FOR UPDATE 時,數(shù)據庫鎖住的遠不止你"看到"的那幾行記錄——它還會把記錄之間的"空地"一并封鎖。這片空地就叫間隙(Gap),而鎖住它的機制,叫做 Gap Lock(間隙鎖)Next-Key Lock(臨鍵鎖)。本文將從"為什么要鎖間隙"出發(fā),一路追到"為什么是左開右閉",把這套機制徹底講透。

一、從一個問題出發(fā):為什么不只鎖行?

在正式講間隙鎖之前,先理解一個核心問題:MySQL 為什么不只鎖住被操作的那幾行記錄,而要額外鎖住行與行之間的空隙?

答案只有一個詞:防止幻讀(Phantom Read)。

什么是幻讀?

幻讀是指在同一個事務內,兩次執(zhí)行相同的范圍查詢,第二次的結果比第一次多出了幾行——就好像憑空出現(xiàn)了"幽靈記錄"一樣。

來看一個具體的例子。假設有一張賬戶表,事務 A 執(zhí)行了如下更新:

UPDATE accounts SET status = 'frozen' WHERE balance < 100;

如果 MySQL 只鎖住當前滿足 balance < 100 的那幾行,而沒有鎖住間隙,那么:

  1. 事務 A 正在掃描并更新符合條件的記錄。
  2. 事務 B 趁機插入了一條新記錄:balance = 50。
  3. 事務 A 更新完成并提交。

結果: 事務 A 明明說要把所有余額小于 100 的賬戶都凍結,卻偏偏漏掉了事務 B 剛插進來的那一行。這條漏網之魚,就是幻讀的典型表現(xiàn)。

為了在可重復讀(Repeatable Read) 隔離級別下徹底消滅幻讀,InnoDB 不得不把整個 balance < 100 的搜索空間全部封鎖,不允許任何新記錄插入其中。這便是間隙鎖存在的根本原因。

二、三種鎖的概念厘清

在深入講解之前,我們先把三個容易混淆的概念理清楚:

鎖類型英文名鎖的對象作用
記錄鎖Record Lock索引上的某一條具體記錄防止其他事務修改或刪除這條記錄
間隙鎖Gap Lock兩條記錄之間的空白間隙防止其他事務在這個間隙內插入新記錄
臨鍵鎖Next-Key Lock間隙 + 右側那條記錄記錄鎖 + 間隙鎖的組合,是 InnoDB 默認的行鎖形式

Next-Key Lock = Gap Lock + Record Lock,這是理解本文的核心公式。

三、唯一索引的特殊待遇:為什么它可以只用記錄鎖?

在講間隙鎖的全貌之前,有必要先說明一個重要的例外情況。

當且僅當同時滿足以下兩個條件時,InnoDB 會將 Next-Key Lock 降級為純粹的 Record Lock,不加間隙鎖:

  1. 搜索條件命中的是唯一索引或主鍵
  2. 搜索方式是等值查詢(使用 =),而不是范圍查詢。

例如:

-- 這是唯一搜索,只加記錄鎖,不加間隙鎖
SELECT * FROM user WHERE id = 10 FOR UPDATE;

為什么唯一索引可以享受這種特殊待遇?

因為唯一索引從定義上保證了 id = 10 這個值在整張表里永遠只能存在一行。只要把這一行鎖住,別人就不可能再插入另一個 id = 10 的記錄進來——幻讀在這里根本就不可能發(fā)生,自然也就不需要鎖間隙了。

反之,以下所有情況都無法享受這種降級,必須使用 Next-Key Lock:

搜索類型示例語句索引類型鎖的行為
唯一搜索(例外)WHERE id = 10主鍵/唯一索引僅記錄鎖(Record Lock)
普通索引搜索WHERE age = 25普通索引臨鍵鎖(Next-Key Lock)
范圍搜索WHERE id > 10任何索引臨鍵鎖(Next-Key Lock)
未命中記錄WHERE id = 11(11不存在)唯一索引間隙鎖(Gap Lock)

四、UPDATE 和 DELETE 也會觸發(fā)范圍鎖

很多人以為只有顯式加鎖的 SELECT ... FOR UPDATE 才會觸發(fā)間隙鎖,其實 UPDATEDELETE 同樣會觸發(fā),而且行為完全一致。

這是因為 UPDATEDELETE 在底層隱含了一個 SELECT ... FOR UPDATE 的過程——它們必須先通過當前讀拿到最新的行數(shù)據,才能在此基礎上進行修改或刪除。

來看一個直觀的例子:

-- 這條語句會鎖住 balance < 100 范圍內所有記錄和間隙
UPDATE accounts SET status = 'frozen' WHERE balance < 100;

執(zhí)行這條語句時,InnoDB 會同時完成兩件事:

  • 行鎖(Record Lock): 鎖住所有當前 balance < 100 的記錄,防止其他事務修改或刪除它們。
  • 間隙鎖(Gap Lock): 鎖住這些記錄之間以及邊界外的空隙,防止其他事務往這個范圍內插入新記錄。

兩者合在一起,就是 Next-Key Lock,對整個搜索范圍實施了"全面封鎖"。

大范圍鎖的代價

理解了這一點,也就理解了為什么大范圍的 UPDATEDELETE 是高并發(fā)場景下的危險操作:

死鎖風險: 兩個事務若嘗試以不同順序鎖定重疊的間隙,極易發(fā)生死鎖。

并發(fā)下降: 大范圍鎖會讓其他想往這個范圍內插入數(shù)據的業(yè)務線程全部進入 Lock Wait 狀態(tài),系統(tǒng)響應急劇變慢。

最佳實踐: 在進行 UPDATEDELETE 時,盡量通過主鍵 ID 進行操作。通過唯一主鍵只會觸發(fā)記錄鎖,不會阻塞整個范圍的插入,性能最優(yōu)。

五、Next-Key Lock 如何劃分區(qū)間?

現(xiàn)在我們來看 InnoDB 是如何將整條索引軸線劃分為一段段"封鎖區(qū)域"的。

區(qū)間的形成規(guī)則

每一條索引記錄都是一個"錨點",記錄與記錄之間自然形成間隙。InnoDB 會像切香腸一樣,沿著索引軸線把整個空間劃分為若干個左開右閉的區(qū)間。

假設索引中存在以下值:10, 11, 13, 20,InnoDB 會劃出如下區(qū)間:

(?∞,  10]
(10,  11]
(11,  13]
(13,  20]
(20, +∞)

每個區(qū)間的含義如下:

區(qū)間鎖定內容
(−∞, 10]鎖住所有小于 10 的插入位置,以及 10 這條記錄本身
(10, 11]鎖住 10 到 11 之間的空隙(禁止插入 10.x),以及 11 這條記錄本身
(11, 13]鎖住 11 到 13 之間的空隙(禁止插入 12),以及 13 這條記錄本身
(13, 20]鎖住 13 到 20 之間的空隙(禁止插入如 15、18),以及 20 這條記錄本身
(20, +∞)鎖住所有大于 20 的插入位置(無上界)

整條索引軸線被無縫、無重疊地分割成了這五段,任何一個插入位置都必然且唯一地落在某一段中。

特殊的最右側區(qū)間

最右側的區(qū)間 (20, +∞) 看起來是一個"開區(qū)間",與其他區(qū)間的左開右閉形式似乎不一致。其實在 InnoDB 內部,它同樣是左開右閉的:(20, Supremum]

InnoDB 在每一棵 B+ 樹索引的內部都維護著兩個虛擬的邊界偽記錄:

  • Infimum:比所有記錄都小的虛擬最小值。
  • Supremum:比所有記錄都大的虛擬最大值。

由于 Supremum 是用戶無法感知的虛擬概念,所以這個區(qū)間在表現(xiàn)上就像一個向右無限延伸的開區(qū)間 (20, +∞)。這意味著,如果你執(zhí)行了 WHERE id > 20 FOR UPDATE,那么所有大于 20 的新數(shù)據(如 21、100、1000……)全部無法插入。

一個實戰(zhàn)案例

假設表中只有 id = 11id = 13 兩條記錄,你執(zhí)行了:

-- id = 12 在表中不存在
SELECT * FROM user WHERE id = 12 FOR UPDATE;

由于 id = 12 這條記錄根本不存在,InnoDB 無法加記錄鎖。于是它轉而鎖定包含 12 的那個 Next-Key 區(qū)間,即 (11, 13]。

這意味著:

  • id = 12 無法被插入(落在被鎖的間隙內)。
  • id = 13 也無法被修改或刪除(被記錄鎖鎖?。?。
  • id = 11 不受影響(不在這個區(qū)間內)。

六、為什么是左開右閉,而不是左閉右開?

理解了區(qū)間的形態(tài)之后,一個更深層的問題浮出水面:InnoDB 為什么選擇 (a, b] 這種左開右閉的形式,而不是 [a, b) 左閉右開?

這背后有三個底層邏輯。

原因一:符合 B+ 樹從左向右的掃描順序

在 B+ 樹索引中,記錄是按升序從小到大排列的,MySQL 掃描時也是從左向右逐條推進的。

當掃描到某條記錄 b 時,InnoDB 需要立刻決定如何加鎖:

  • 鎖住記錄 b 本身(記錄鎖)。
  • 鎖住 b 左側的間隙,即 (a, b) 這段空地(間隙鎖)。

這兩個動作合并在一起,自然就形成了 (a, b] 的左開右閉區(qū)間。

如果采用左閉右開 [a, b),則意味著掃描到記錄 a 時,需要去鎖住 a 右側的間隙——你還沒"走到"右邊,卻要預先聲稱鎖住右邊的空地,這與 B+ 樹從左到右掃描的順序是背離的,邏輯上非常別扭。

原因二:與 Supremum 偽記錄天然契合

使用左開右閉時,最后一個區(qū)間可以完美地表示為 (最大記錄值, Supremum],用統(tǒng)一的閉區(qū)間邏輯封死了索引右側的所有插入可能。

如果采用左閉右開,則最后一個區(qū)間將是 [Supremum, …)——但 Supremum 已經是最大邊界,它右邊根本沒有任何間隙,這個表達式在邏輯上完全行不通。

原因三:每條記錄都明確成為一個區(qū)間的唯一錨點

左開右閉的設計讓每一條索引記錄都清晰地成為且僅成為一個區(qū)間的右終點

(a, b] 中,記錄 b 是整個 Next-Key Lock 的"錨點"——當你申請鎖定記錄 b 時,InnoDB 順帶就把 b 左邊的間隙一并管轄起來。這種設計保證了一個至關重要的特性:整條索引軸線上,每一個可能插入新數(shù)據的位置,都屬于且僅屬于一個 Next-Key Lock 區(qū)間,沒有任何遺漏,也沒有任何重疊。

設計對比總結

維度左開右閉 (a, b](MySQL 選擇)左閉右開 [a, b)
錨點關系記錄 b 負責它左側的間隙記錄 a 負責它右側的間隙
掃描邏輯符合 B+ 樹從左到右的掃描順序邏輯滯后,掃到 a 卻要鎖右邊
邊界處理完美兼容 Supremum 偽記錄無法自然處理無窮大邊界
鎖定本質間隙鎖 + 記錄鎖的自然結合記錄鎖 + 后置間隙鎖,邏輯拆散

一句話總結: 左開右閉是為了讓記錄鎖(Record Lock)能順理成章地作為它左側間隙(Gap Lock)的守護者,從而在 B+ 樹升序掃描的過程中,實現(xiàn)對索引軸線的無死角、無重疊覆蓋。

七、完整總結

概念核心含義
間隙鎖(Gap Lock)鎖住兩條記錄之間的空地,禁止插入新記錄,是防幻讀的核心武器
記錄鎖(Record Lock)鎖住某條具體的索引記錄,防止修改或刪除
臨鍵鎖(Next-Key Lock)Gap Lock + Record Lock 的組合,是 InnoDB 在 RR 級別下的默認鎖形式
唯一索引等值查詢唯一例外,降級為純記錄鎖,不加間隙鎖,因為唯一性保證不會有幻讀
UPDATE / DELETE隱含當前讀,同樣會觸發(fā) Next-Key Lock,大范圍操作需特別謹慎
左開右閉區(qū)間InnoDB 劃分鎖區(qū)間的統(tǒng)一形式,符合 B+ 樹掃描順序,無死角覆蓋整條索引軸線
Supremum 偽記錄B+ 樹內部的虛擬最大邊界,使最右側區(qū)間 (x, +∞) 在內部同樣以閉區(qū)間表示

間隙鎖機制是 InnoDB 在不犧牲并發(fā)性能的前提下,消滅幻讀這一頑固問題的精巧解法。理解它的區(qū)間劃分方式,不僅能幫你看懂那些"莫名其妙"的插入等待,更能讓你在設計高并發(fā)業(yè)務時主動規(guī)避范圍鎖陷阱,寫出真正高效的數(shù)據庫操作。

到此這篇關于MySQL 鎖機制深度解析:間隙鎖、Next-Key Lock 與幻讀防御的文章就介紹到這了,更多相關mysql間隙鎖、Next-Key Lock 與幻讀防御內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • MySQL里面的子查詢實例

    MySQL里面的子查詢實例

    最近學習php+mysql執(zhí)行操作,發(fā)現(xiàn)了這一篇實例代碼
    2008-04-04
  • 詳解MySQL8.0​ 字典表增強

    詳解MySQL8.0​ 字典表增強

    這篇文章主要介紹了MySQL8.0&#8203; 字典表增強的相關資料,幫助大家更好的理解和學習MySQL,感興趣的朋友可以了解下
    2020-08-08
  • windows10下mysql 8.0 下載與安裝配置圖文教程

    windows10下mysql 8.0 下載與安裝配置圖文教程

    這篇文章主要介紹了windows10下mysql 8.0 下載與安裝配置圖文教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • MySQL循環(huán)查詢的實現(xiàn)示例

    MySQL循環(huán)查詢的實現(xiàn)示例

    MySQL循環(huán)查詢是指在MySQL數(shù)據庫中使用循環(huán)結構進行數(shù)據查詢的一種方法,本文主要介紹了MySQL循環(huán)查詢的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-07-07
  • MySQL修改tmpdir參數(shù)

    MySQL修改tmpdir參數(shù)

    本文給大家分享的是在linux系統(tǒng)下MySQL修改tmpdir參數(shù)解決tmpdir報錯的問題,有相同需求的小伙伴可以參考下
    2016-02-02
  • MySQL中如何重建表

    MySQL中如何重建表

    這篇文章主要介紹了MySQL中如何重建表問題。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 尋找sql注入的網站的方法(必看)

    尋找sql注入的網站的方法(必看)

    下面小編就為大家?guī)硪黄獙ふ襰ql注入的網站的方法(必看)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • mysql日期處理函數(shù)實例解析

    mysql日期處理函數(shù)實例解析

    這篇文章主要介紹了mysql日期處理函數(shù)實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • MySQL日志系統(tǒng)之錯誤日志、慢查詢日志、二進制日志詳解

    MySQL日志系統(tǒng)之錯誤日志、慢查詢日志、二進制日志詳解

    MySQL日志系統(tǒng)是數(shù)據庫管理的重要組成部分,它幫助數(shù)據庫管理員監(jiān)控數(shù)據庫活動、優(yōu)化查詢、恢復數(shù)據以及診斷問題,這篇文章主要介紹了MySQL日志系統(tǒng)之錯誤日志、慢查詢日志、二進制日志的相關資料,需要的朋友可以參考下
    2025-05-05
  • MySQL的源碼安裝及使用UDFs進行數(shù)據自動更新的教程

    MySQL的源碼安裝及使用UDFs進行數(shù)據自動更新的教程

    UDFs即是MySQL的用戶自定義函數(shù)的縮寫,配合觸發(fā)器可以自動更新Memcached與MySql的數(shù)據,這里我們就來總結一下MySQL的源碼安裝及使用UDFs進行數(shù)據自動更新的教程:
    2016-07-07

最新評論

新民市| 凤庆县| 高碑店市| 德化县| 克山县| 甘德县| 九龙坡区| 班玛县| 平顶山市| 邯郸市| 华阴市| 元朗区| 五台县| 济源市| 万州区| 黔东| 西乌珠穆沁旗| 泾阳县| 石柱| 南丰县| 金乡县| 黄冈市| 藁城市| 错那县| 辰溪县| 奇台县| 略阳县| 禹城市| 和龙市| 天水市| 金堂县| 丰原市| 义乌市| 清新县| 盐山县| 城步| 奉化市| 中超| 中宁县| 丹东市| 玛曲县|