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

淺談MySQL InnoDB實(shí)現(xiàn)MVCC原理

 更新時(shí)間:2026年03月06日 10:17:34   作者:蕭曵 丶  
本文主要介紹了MySQL InnoDB實(shí)現(xiàn)MVCC原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

核心價(jià)值先記?。篗VCC 的終極目標(biāo)是實(shí)現(xiàn)「讀不加鎖,讀寫互不阻塞」,極大提升數(shù)據(jù)庫的并發(fā)讀寫性能,這也是 InnoDB 能替代 MyISAM 的核心原因之一。

一、先搞懂:為什么需要 MVCC?(MVCC 的誕生意義)

在 MVCC 出現(xiàn)之前,數(shù)據(jù)庫的并發(fā)控制只有兩種方式,都有致命缺陷:

  1. 加鎖查詢:讀操作加共享鎖,寫操作加排他鎖 → 讀和寫互相阻塞,并發(fā)性能極低;
  2. 無鎖查詢:不加鎖直接讀 → 會出現(xiàn)臟讀、不可重復(fù)讀、幻讀等事務(wù)隔離性問題,數(shù)據(jù)一致性無法保證。

? MVCC 完美解決了這個(gè)矛盾:

MVCC 是一種無鎖的并發(fā)控制機(jī)制,對讀操作(普通 SELECT)完全不加鎖,對寫操作只加行級鎖;讀操作不會阻塞寫操作,寫操作也不會阻塞讀操作,同時(shí)還能保證不同事務(wù)的隔離性,精準(zhǔn)解決臟讀、不可重復(fù)讀、幻讀問題。

二、MVCC 核心前置知識(必須掌握,3 個(gè)根基,缺一不可)

MVCC 的實(shí)現(xiàn)沒有任何黑魔法,完全依賴 InnoDB 三個(gè)底層核心設(shè)計(jì)組合實(shí)現(xiàn),這三個(gè)是理解 MVCC 的絕對前提,所有原理都是基于這三點(diǎn)展開:

? 根基 1:InnoDB 每行數(shù)據(jù)的「3 個(gè)隱藏字段」(版本核心標(biāo)識)

InnoDB 存儲引擎中,我們建表時(shí)定義的每一行數(shù)據(jù),在磁盤實(shí)際存儲時(shí),都會自動額外添加 3 個(gè)隱藏字段(無需手動定義,引擎自動維護(hù)),這是行數(shù)據(jù)的版本號核心標(biāo)識,重中之重!

每行數(shù)據(jù)的物理存儲 = 我們定義的列 + 3個(gè)隱藏字段

三個(gè)隱藏字段的作用(MySQL 8.0/5.7 通用):

  1. db_trx_id 【6 字節(jié)】事務(wù) ID:當(dāng)前行數(shù)據(jù)的最后一次修改 / 插入的事務(wù) ID,是自增的唯一值(事務(wù)開啟時(shí),InnoDB 會分配一個(gè)全局唯一的遞增事務(wù) ID);
    • 插入一行:該行的db_trx_id = 插入事務(wù)的 ID;
    • 更新一行:該行的db_trx_id = 更新事務(wù)的 ID(更新本質(zhì)是「標(biāo)記舊行刪除 + 插入新行」);
    • 刪除一行:該行的db_trx_id = 刪除事務(wù)的 ID(刪除本質(zhì)是「標(biāo)記刪除」);
  2. db_roll_ptr 【7 字節(jié)】回滾指針:指向當(dāng)前行數(shù)據(jù)對應(yīng)的 undo log 回滾日志 的地址,通過這個(gè)指針可以找到該行的「歷史版本數(shù)據(jù)」;
  3. db_row_id 【6 字節(jié)】行 ID:可選隱藏字段,只有當(dāng)表沒有主鍵、也沒有唯一非空索引時(shí),InnoDB 才會自動生成這個(gè)字段,作為聚簇索引。有主鍵的表不會生成這個(gè)字段。

? 根基 2:Undo Log 回滾日志 & 版本鏈(歷史版本的載體)

① 什么是 Undo Log

Undo Log(回滾日志)是 InnoDB 事務(wù)四大日志之一(redo/undo/binlog/error log),屬于邏輯日志,作用有兩個(gè):

  • 事務(wù)回滾:事務(wù)執(zhí)行失敗時(shí),通過 undo log 恢復(fù)數(shù)據(jù)到修改前的狀態(tài);
  • 支撐 MVCC:存儲行數(shù)據(jù)的歷史版本,供其他事務(wù)做「一致性讀」。

② 版本鏈的形成(核心結(jié)構(gòu))

基于「隱藏字段db_roll_ptr + undo log」,InnoDB 會為每行數(shù)據(jù)生成一條 版本鏈,規(guī)則如下:

  1. 當(dāng)事務(wù)對某行數(shù)據(jù)執(zhí)行插入 / 更新 / 刪除操作時(shí),會先把該行數(shù)據(jù)的「舊版本」寫入到 undo log 中;
  2. 該行數(shù)據(jù)的隱藏字段 db_roll_ptr 會指向這條 undo log 的地址;
  3. 如果該行數(shù)據(jù)被多次修改,則會生成多條 undo log,這些 undo log 通過 db_roll_ptr 指針首尾相連,形成一條版本鏈;
  4. 版本鏈的頭節(jié)點(diǎn)是數(shù)據(jù)的「最新版本」(存儲在聚簇索引的葉子節(jié)點(diǎn)),版本鏈的后續(xù)節(jié)點(diǎn)是數(shù)據(jù)的「歷史版本」(存儲在 undo log 中)。

? 核心結(jié)論:版本鏈中,越往后的版本,事務(wù) ID 越小(數(shù)據(jù)越舊)

版本鏈結(jié)構(gòu)示意圖(一目了然)

【聚簇索引中存儲的 最新版本數(shù)據(jù)】
行記錄(最新):col1=1, col2=2 | db_trx_id=50 | db_roll_ptr → 指向undo log版本40
          ↑
          |
【undo log 中的 歷史版本鏈】
undo log版本40:col1=1, col2=1 | db_trx_id=40 | db_roll_ptr → 指向undo log版本30
          ↑
          |
undo log版本30:col1=0, col2=1 | db_trx_id=30 | db_roll_ptr = null (最早版本)

? 根基 3:InnoDB 的「非鎖定讀」(MVCC 的讀模式,核心)

InnoDB 對 SELECT 查詢提供了兩種讀模式,MVCC 依賴的是非鎖定讀,這是 InnoDB 的默認(rèn)讀模式

  1. 鎖定讀select ... for update / select ... lock in share mode,會加行鎖 / 共享鎖,讀寫阻塞,一般用于寫多讀少場景;
  2. 非鎖定讀:普通的 SELECT * FROM table完全不加鎖,這是我們?nèi)粘i_發(fā) 99% 的查詢方式,也是 MVCC 的核心載體;
    1. 核心邏輯:非鎖定讀時(shí),InnoDB 會通過「版本鏈」讀取行數(shù)據(jù)的某個(gè)歷史版本,而不是最新版本,從而實(shí)現(xiàn)「讀不加鎖,讀寫不阻塞」。

三、MVCC 最核心的實(shí)現(xiàn):ReadView(讀視圖,可見性規(guī)則)

面試必考核心:MVCC 的核心就是「版本鏈」 + 「ReadView」,版本鏈提供了數(shù)據(jù)的歷史版本,ReadView 提供了版本的可見性判斷規(guī)則。

? 3.1 什么是 ReadView(讀視圖)

   ReadView 翻譯成「讀視圖 / 一致性視圖」,是事務(wù)在執(zhí)行查詢操作時(shí),生成的一個(gè)「當(dāng)前數(shù)據(jù)庫中活躍事務(wù)的快照」。

  • 「活躍事務(wù)」:指的是當(dāng)前已經(jīng)開啟但還未提交的事務(wù)
  • 「快照」:生成后就不會再變,是一個(gè)靜態(tài)的視圖;
  • 生成時(shí)機(jī):不同的事務(wù)隔離級別,生成 ReadView 的時(shí)機(jī)完全不同(這是解決臟讀 / 不可重復(fù)讀 / 幻讀的關(guān)鍵,后文重點(diǎn)講)。

? 3.2 ReadView 的 4 個(gè)核心字段(固定結(jié)構(gòu))

每個(gè) ReadView 內(nèi)部都維護(hù)了 4 個(gè)核心字段,這 4 個(gè)字段是可見性判斷的全部依據(jù),無任何多余字段:

class ReadView {
    // 1. 當(dāng)前系統(tǒng)中,所有「活躍事務(wù)」的事務(wù)ID集合(已開啟未提交)
    private Set<Long> m_ids;
    // 2. 活躍事務(wù)中,最小的事務(wù)ID
    private Long min_trx_id;
    // 3. 生成該ReadView時(shí),系統(tǒng)「下一個(gè)要分配的事務(wù)ID」(即當(dāng)前最大的事務(wù)ID+1)
    private Long max_trx_id;
    // 4. 生成該ReadView的「當(dāng)前事務(wù)」的事務(wù)ID
    private Long creator_trx_id;
}

? 3.3 【重中之重】行版本的「可見性判斷規(guī)則」

這是 MVCC 的靈魂邏輯,也是面試的必考點(diǎn),所有的隔離性保證都源于這套規(guī)則。

核心流程:事務(wù)執(zhí)行普通 SELECT 時(shí),生成 ReadView → 從版本鏈中讀取行數(shù)據(jù)的版本 → 用這套規(guī)則判斷「該版本的數(shù)據(jù)是否對當(dāng)前事務(wù)可見」。

判斷規(guī)則(對版本鏈中的某一行版本數(shù)據(jù),依次判斷,有一個(gè)滿足即可):假設(shè) 當(dāng)前待判斷的行版本的事務(wù) ID = trx_id,當(dāng)前 ReadView 的字段如上;

  1. ? 規(guī)則 1:如果 trx_id < ReadView.min_trx_id→ 說明這個(gè)版本的數(shù)據(jù)是由「已經(jīng)提交的事務(wù)」修改的,數(shù)據(jù)可見;
  2. ? 規(guī)則 2:如果 trx_id >= ReadView.max_trx_id→ 說明這個(gè)版本的數(shù)據(jù)是由「在當(dāng)前事務(wù)開啟后才啟動的事務(wù)」修改的,數(shù)據(jù)不可見;
  3. ? 規(guī)則 3:如果 min_trx_id ≤ trx_id < max_trx_id 且 trx_id ∈ m_ids→ 說明這個(gè)版本的數(shù)據(jù)是由「當(dāng)前活躍的未提交事務(wù)」修改的,數(shù)據(jù)不可見;
  4. ? 規(guī)則 4:如果 min_trx_id ≤ trx_id < max_trx_id 且 trx_id ∉ m_ids→ 說明這個(gè)版本的數(shù)據(jù)是由「在當(dāng)前事務(wù)開啟前已提交的事務(wù)」修改的,數(shù)據(jù)可見。

? 不可見的處理邏輯

如果當(dāng)前版本的數(shù)據(jù)不可見,則通過該行的 db_roll_ptr 指針,去版本鏈中讀取上一個(gè)歷史版本,然后重復(fù)執(zhí)行上述 4 條規(guī)則,直到找到第一個(gè)可見的版本,如果版本鏈遍歷完都沒有可見版本,則返回空。

四、【核心面試考點(diǎn)】不同隔離級別下 MVCC 的實(shí)現(xiàn)差異

面試必問的:為什么 RC 能解決臟讀,RR 能解決不可重復(fù)讀和幻讀?本質(zhì)是「生成 ReadView 的時(shí)機(jī)不同」。

前提:MySQL 默認(rèn)事務(wù)隔離級別是 RR(可重復(fù)讀),另一個(gè)常用級別是 RC(讀已提交);這兩個(gè)級別都基于 MVCC 實(shí)現(xiàn),而「讀未提交 / 串行化」不依賴 MVCC。

? 核心結(jié)論(先記死,面試必答)

  1. RC(讀已提交):每次執(zhí)行 SELECT 查詢時(shí),都會生成一個(gè)新的 ReadView;
  2. RR(可重復(fù)讀):事務(wù)中第一次執(zhí)行 SELECT 查詢時(shí),生成唯一的一個(gè) ReadView,之后整個(gè)事務(wù)的所有查詢都復(fù)用這個(gè) ReadView;

? 4.1 案例 1:RR(可重復(fù)讀)的實(shí)現(xiàn)過程(解決不可重復(fù)讀)

場景模擬

  • 事務(wù) A(trx_id=10):隔離級別 RR,執(zhí)行查詢 SELECT name FROM user WHERE id=1;
  • 事務(wù) B(trx_id=20):開啟事務(wù),更新 user 表 id=1 的 name 為 "李四",但未提交
  • 事務(wù) C(trx_id=30):開啟事務(wù),更新 user 表 id=1 的 name 為 "王五",提交事務(wù)。

執(zhí)行流程

  1. 事務(wù) A 第一次執(zhí)行 SELECT → 生成唯一的 ReadView
    • m_ids={20}(事務(wù) B 活躍未提交)、min_trx_id=20、max_trx_id=31、creator_trx_id=10;
  2. 讀取行數(shù)據(jù)最新版本,trx_id=30(事務(wù) C 提交),根據(jù)規(guī)則判斷:30 <31 且 30 ∉ {20} → 可見,返回 name="王五";
  3. 此時(shí)事務(wù) B 提交(trx_id=20),事務(wù) A再次執(zhí)行相同的 SELECT復(fù)用第一次的 ReadView,判斷規(guī)則不變;
  4. 即使數(shù)據(jù)有新的版本,事務(wù) A 讀取的結(jié)果還是 name="王五",兩次查詢結(jié)果一致 → 解決了「不可重復(fù)讀」。

? 4.2 案例 2:RC(讀已提交)的實(shí)現(xiàn)過程(存在不可重復(fù)讀)

同樣的場景,事務(wù)隔離級別改為 RC:

  1. 事務(wù) A 第一次執(zhí)行 SELECT → 生成 ReadView1,判斷后返回 name="王五";
  2. 事務(wù) B 提交后,事務(wù) A再次執(zhí)行 SELECT生成新的 ReadView2,此時(shí) m_ids 為空,min_trx_id=31;
  3. 讀取最新版本數(shù)據(jù),trx_id=20 < 31 → 可見,返回 name="李四";
  4. 兩次查詢結(jié)果不一致 → 存在「不可重復(fù)讀」,但解決了「臟讀」。

? 為什么 RR 能解決幻讀?

RR 級別下,因?yàn)檎麄€(gè)事務(wù)復(fù)用同一個(gè) ReadView,所以無論其他事務(wù)插入 / 刪除多少數(shù)據(jù),當(dāng)前事務(wù)都看不到,因?yàn)樾虏迦氲臄?shù)據(jù)的 trx_id >= max_trx_id,永遠(yuǎn)不可見 → 完美解決「幻讀」。

五、補(bǔ)充:MVCC 對 DELETE/INSERT 的處理邏輯

? 1. DELETE 操作

InnoDB 中沒有真正的物理刪除,執(zhí)行 DELETE 時(shí),只是給該行數(shù)據(jù)打上一個(gè)「刪除標(biāo)記」,并把該行的 db_trx_id 更新為刪除事務(wù)的 ID;

  • 對其他事務(wù)來說,通過可見性規(guī)則判斷,這個(gè)被標(biāo)記的版本是不可見的,就相當(dāng)于「刪除了」;
  • 物理刪除是在后續(xù)的「垃圾回收(purge)」階段,由 InnoDB 后臺線程清理掉這些不可見的版本。

? 2. INSERT 操作

插入一行數(shù)據(jù)時(shí),會生成一條新的版本鏈頭節(jié)點(diǎn),該行的 db_trx_id 是插入事務(wù)的 ID;

  • 未提交的插入,對其他事務(wù)不可見;提交后的插入,對其他事務(wù)是否可見,依然遵循可見性規(guī)則。

? 3. UPDATE 操作

InnoDB 中更新本質(zhì)是「寫時(shí)復(fù)制」:執(zhí)行 UPDATE 時(shí),不會直接修改原行數(shù)據(jù),而是:

  1. 把原行數(shù)據(jù)的舊版本寫入 undo log,形成版本鏈;
  2. 插入一條新的行數(shù)據(jù)(新版本),更新 db_trx_id 為當(dāng)前事務(wù) ID;
  3. 原行數(shù)據(jù)被標(biāo)記為「刪除狀態(tài)」,后續(xù)由 purge 線程清理。

六、MVCC 的優(yōu)缺點(diǎn) & 適用場景(面試加分)

? 優(yōu)點(diǎn)(為什么 MVCC 是最優(yōu)解)

  1. 極致的并發(fā)性能:讀不加鎖,讀寫互不阻塞,這是最大的優(yōu)勢,高并發(fā)場景下性能碾壓加鎖查詢;
  2. 保證事務(wù)隔離性:RC/RR 級別下,完美解決臟讀、不可重復(fù)讀、幻讀(RR),兼顧性能和一致性;
  3. 無鎖開銷:不需要維護(hù)鎖的申請、釋放、等待,減少了數(shù)據(jù)庫的鎖競爭和上下文切換開銷;

? 缺點(diǎn)(MVCC 的代價(jià))

  1. 存儲開銷:需要存儲 undo log 和版本鏈,會占用額外的磁盤空間;
  2. CPU 開銷:查詢時(shí)需要遍歷版本鏈 + 執(zhí)行可見性判斷,有少量的 CPU 計(jì)算開銷;
  3. 清理開銷:InnoDB 需要后臺 purge 線程清理過期的 undo log 版本,有一定的后臺開銷;

? 適用場景

99% 的業(yè)務(wù)場景都適用 MVCC,尤其是:

  • 讀多寫少的高并發(fā)場景(如電商列表、資訊詳情、后臺報(bào)表);
  • 不需要實(shí)時(shí)讀取最新數(shù)據(jù),能接受短時(shí)間數(shù)據(jù)一致性的場景;

例外:如果是寫多讀少,且要求實(shí)時(shí)讀取最新數(shù)據(jù)(如金融轉(zhuǎn)賬、庫存扣減),建議用鎖定讀。

七、MVCC 核心知識點(diǎn)總結(jié)(面試必背清單,精華無冗余)

  1. MVCC 的全稱是多版本并發(fā)控制,是 InnoDB 的無鎖并發(fā)控制機(jī)制,核心目標(biāo)是讀不加鎖,讀寫互不阻塞;
  2. MVCC 的實(shí)現(xiàn)依賴三個(gè)核心:行的 3 個(gè)隱藏字段、undo log 版本鏈、ReadView 讀視圖
  3. 每行數(shù)據(jù)的 db_trx_id 是最后一次修改的事務(wù) ID,db_roll_ptr 指向 undo log 形成版本鏈;
  4. ReadView 是事務(wù)查詢時(shí)的活躍事務(wù)快照,包含 4 個(gè)核心字段,是版本可見性的判斷依據(jù);
  5. 可見性判斷規(guī)則是 MVCC 的靈魂,核心是判斷行版本的 trx_id 和 ReadView 的關(guān)系;
  6. RC 和 RR 的核心差異是生成 ReadView 的時(shí)機(jī)不同:RC 每次查詢生成新的,RR 事務(wù)內(nèi)復(fù)用一個(gè);
  7. RR 能解決幻讀的本質(zhì)是:事務(wù)內(nèi)復(fù)用同一個(gè) ReadView,看不到其他事務(wù)的插入 / 刪除;
  8. InnoDB 的 delete 是邏輯刪除,update 是寫時(shí)復(fù)制,都依賴版本鏈實(shí)現(xiàn);
  9. MVCC 只適用于 RC/RR 隔離級別,讀未提交和串行化不依賴 MVCC。

最終總結(jié)

MVCC 不是一個(gè)單一的技術(shù),而是 InnoDB 把「隱藏字段、版本鏈、undo log、ReadView」這幾個(gè)底層設(shè)計(jì)精妙組合的產(chǎn)物,它的核心思想是:通過為每行數(shù)據(jù)維護(hù)多個(gè)版本,讓讀操作可以讀取歷史版本,從而避免加鎖,實(shí)現(xiàn)讀寫并發(fā)。

理解 MVCC 的原理,不僅能回答面試中的核心問題,更能讓你在實(shí)際開發(fā)中,針對不同的業(yè)務(wù)場景選擇合適的事務(wù)隔離級別和查詢方式,寫出高性能的 SQL 語句,這也是高級開發(fā)和架構(gòu)師的必備功底。

到此這篇關(guān)于淺談MySQL InnoDB實(shí)現(xiàn)MVCC原理的文章就介紹到這了,更多相關(guān)MySQL InnoDB MVCC內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysqldump備份數(shù)據(jù)庫時(shí)排除某些庫的實(shí)例

    mysqldump備份數(shù)據(jù)庫時(shí)排除某些庫的實(shí)例

    下面小編就為大家?guī)硪黄猰ysqldump備份數(shù)據(jù)庫時(shí)排除某些庫的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-03-03
  • MySQL大表數(shù)據(jù)的分區(qū)與分庫分表的實(shí)現(xiàn)

    MySQL大表數(shù)據(jù)的分區(qū)與分庫分表的實(shí)現(xiàn)

    數(shù)據(jù)庫的分區(qū)和分庫分表是兩種常用的技術(shù)方案,本文主要介紹了MySQL大表數(shù)據(jù)的分區(qū)與分庫分表的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • MySQL自定義函數(shù)和存儲過程示例詳解

    MySQL自定義函數(shù)和存儲過程示例詳解

    這篇文章主要給大家介紹了關(guān)于MySQL自定義函數(shù)和存儲過程的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用MySQL具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • ubuntu16.04.1下 mysql安裝和卸載圖文教程

    ubuntu16.04.1下 mysql安裝和卸載圖文教程

    這篇文章主要介紹了ubuntu16.04.1下 mysql安裝和卸載圖文教程,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-11-11
  • MySQL?賬號密碼錯(cuò)誤終極解決方法

    MySQL?賬號密碼錯(cuò)誤終極解決方法

    這篇文章主要介紹了MySQL?賬號密碼錯(cuò)誤終極解決方法,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • MYSQL增加索引語句小結(jié)

    MYSQL增加索引語句小結(jié)

    這篇文章主要給大家介紹了關(guān)于MYSQL增加索引的相關(guān)資料,索引是一種特殊的文件(InnoDB數(shù)據(jù)表上的索引是表空間的一個(gè)組成部分),它們包含著對數(shù)據(jù)表里所有記錄的引用指針,需要的朋友可以參考下
    2023-09-09
  • 最新評論

    泗阳县| 伊川县| 淮南市| 武隆县| 方山县| 望奎县| 孝义市| 镇原县| 民丰县| 金堂县| 新蔡县| 盐城市| 开化县| 绥芬河市| 文登市| 灌云县| 甘孜| 瑞丽市| 长寿区| 内乡县| 阜宁县| 通渭县| 光山县| 长治市| 巫山县| 专栏| 册亨县| 清水河县| 民乐县| 四川省| 东乡族自治县| 平定县| 尚志市| 哈密市| 龙里县| 德昌县| 泾源县| 德清县| 封开县| 日照市| 同德县|