超詳細解讀:數據庫MVCC機制

之前文章:Mysql鎖_exclusivelock for update寫鎖-CSDN博客? ?中有提到通過MVCC來實現快照讀,從而解決幻讀問題,這里詳細介紹下MVCC。

一、前言

表1:實例表t
idk
11
22
表2:事務A、B、C的執行流程
事務A事務B事務C
start transaction with consistent snaption
start transaction with consistent snaption
update t set k=k+1 where id =1

update t set k=k+1 where id =1;

select k from t where id =1;

select k from t where id =1;

commit;

commit

先看上面執行流程,先思考下事務A和B兩次查詢結果都是什么。

注:

1、begin/start transaction 命令并不是一個事務的起點,在執行到它們之后的第一個操作InnoDB表的語句(第一個快照讀語句),事務才真正啟動。這里使用start transaction with consistent snaption命令立即開始事務。

2、事務C沒有使用命令開啟事務,因為update語句本身就是一個事務,執行完畢后執行commit

二、MVCC 的核心思想

????????MVCC 的核心是通過?數據多版本?和?一致性視圖(Consistent Read View)?來實現高并發下的讀寫隔離。其核心思想是:

  1. 每個數據行有多個版本,每次更新生成新版本,舊版本通過 undo log 保留。

  2. 事務根據可見性規則判斷應讀取哪個版本,而非直接讀取最新數據。

    1. 事務ID與行版本

    • 事務ID(Transaction ID):每個事務啟動時,InnoDB會為其分配一個全局唯一且遞增的ID(trx_id)。

    • 行數據的版本:每次事務修改數據時,會生成一個新的數據版本,并將事務ID記錄在該版本的?row trx_id?字段中。舊版本的數據通過?Undo Log?保存,形成版本鏈。

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖1:行狀態變更圖

    ????????上圖就是一個記錄被多個事務連續更新后的狀態。圖中虛線框里是同一行數據的4個版本,當前最新版本是V4,k的值是22,它是被transaction id 為25的事務更新的,因此它的row trx_id也是25。U1,U2,U3則是undo log的記錄的日志。

    2. 一致性讀視圖(Consistent Read View)

    ???????事務啟動時(RR 級別)或語句執行時(RC 級別),InnoDB 會生成一個?一致性視圖,用于判斷數據版本的可見性。

    Read View 的四大核心屬性

    1. trx_ids(活躍事務 ID 集合)

    • 含義:生成 Read View 時,當前系統中所有未提交的活躍事務 ID 的集合。
    • 作用:用于判斷數據版本的事務是否在 Read View 生成時處于活躍狀態。若在集合中,則該版本對當前事務不可見(除了自身事務,自身事務對于表的修改對于自己當然是可見的)。

    2. up_limit_id(最小活躍事務 ID)

    • 含義trx_ids集合中的最小事務 ID。
    • 作用:若數據版本的事務 ID < low_trx_id → 該版本在 Read View 生成前已提交,可見。

    3. low_limit_id(最大事務 ID 上限)

    • 含義:生成 Read View 時,系統中尚未分配的下一個事務 ID(并非實際存在的事務 ID)。
    • 作用:若數據版本的事務 ID ≥ up_trx_id → 該版本在 Read View 生成后才被創建,不可見。

    4.?creator_trx_id(當前事務 ID)

    • 含義:生成該 Read View 的當前事務 ID。
    • 作用:若數據版本的事務 ID == creator_trx_id → 當前事務自己修改的數據,可見。

    數據可見性判斷規則

    當事務讀取一行數據時,需根據以下條件判斷版本是否可見:

    1. 版本事務 ID < up_limit_id?→ 可見(已提交且早于 Read View 生成)。
    2. 版本事務 ID >= low_limit_id?→ 不可見(生成時間晚于 Read View)。
    3. 版本事務 ID 在 [up_limit_id, low_limit_id) 區間內
      • 若在 trx_ids中 → 不可見(活躍未提交)。
      • 若不在 trx_ids中 → 可見(已提交且在 Read View 生成后提交)。
    4. 版本事務 ID == creator_trx_id?→ 可見(當前事務自己修改的)。

    注意:一旦一個Read View被創建,這三個參數將不再發生變化,其中low_limit_id 和 up_limit_id分別是 trx_Ids數組的上下界(注意:從單詞上來區分的話很容易弄反)。

    三、MVCC 如何實現隔離級別

    1. 可重復讀(RR)

    • 視圖創建時機:事務啟動時創建一致性視圖,后續所有讀操作基于此視圖。

    • 效果:事務內看到的數據始終一致,不受其他事務提交的影響。

    示例分析:

    如上面表格2中:

    假設事務開始前,當前活躍事務id=99,則事務A、B、C的事務id依次是100、101、102,事務開始前id=1 k=1的這一行數據的row trx_ids是90。(版本V1)

    那么,我們看下在 RR 隔離級別下執行過程:

    視圖建立時,事務A的trx_ids=[99,100],同樣事務B的trx_ids=[99,100,101]、事務C的trx_ids=[99,100,101,102]。先執行事務C的update,當前版本從V1(k=1)變成V2(k=2),V1則變為歷史版本,執行事務B的update,歷史版本從V2(k=2)變成V3(k=3),V2變成歷史版本。

    這里為啥執行事務B,從k=2變成3,不是事務隔離嗎?這個我們在后面解釋。

    事務B執行查詢操作時:

    trx_ids: [99,100,101]

    up_limit_id: [99]

    low_limit_id: [102]

    creator_trx_id=101,先查看V3版本,事務id=101在trx_ids中,但是等于我們當前事務,所以可見,所以最后結果k=3?

    事務A執行查詢操作時:

    trx_ids: [99,100]

    up_limit_id: [99]

    low_limit_id: [101]

    creator_trx_id=100,先查看V3版本,事務id=101 >=?low_limit_id 不可見,然后查找V2版本,事務id=102 >=?low_limit_id,不可見,在查找V1版本,事務id=90,不在trx_ids中,可見。所有我們通過undo log,從V3 -> V2 -> V1,我們獲取數據,最后結果k=1

    這樣執行下來,雖然期間這一行數據被修改過,但是事務A不論在什么時候查詢,看到這行數據的結果都是一致的,所以我們稱之為一致性讀

    2. 讀提交(RC)

    • 視圖創建時機:每條語句執行前重新生成一致性視圖。

    • 效果:每次查詢能看到已提交的最新數據。

    示例分析

    表2中的“start transaction with consistent snapshot; ”的意思是從這個語句開始,創建一個持續整個事務的一致性快照。所以,在讀提交隔離級別下,這個用法就沒意義了,等效于普通的start transaction。

    在 RC 隔離級別下:

    事務B的查詢結果和RR一致。

    • 事務 C?已提交(ID=102),不在活躍事務數組中。

    • 事務 B?未提交(ID=101),仍在活躍事務數組中。

    • 事務 A?執行查詢時:

      • trx_ids:[101](僅事務 B 未提交)。

      • up_limit_id:101(活躍事務最小 ID)。

      • low_limit_id:103(當前最大事務 ID=102,+1 后為 103)。

      數據可見性判斷

      • 若數據的最新版本由事務 B(ID=101)更新:

        • row trx_id=101?在活躍數組中,不可見

        • 繼續查找歷史版本,找到事務 C(ID=102)提交的版本:

          • row trx_id=102 < 高水位(103),且不在活躍數組中,可見

      • 因此,事務 A 的第一次查詢會讀到事務 C 提交后的數據,即k=2。

    如果事務B提交后,事務A再執行一次查詢呢?

    ?事務 B 提交后

    • 事務 B?提交后,ID=101 不再屬于活躍事務。

    • 事務 A?執行第二次查詢:

      • 活躍事務數組[](無未提交事務)。

      • 低水位:無(數組為空)。

      • 高水位:103(最大事務 ID 仍為 102,+1 后為 103)。

      數據可見性判斷

      • 數據的最新版本由事務 B(ID=101)提交:

        • row trx_id=101 < 高水位(103),且不在活躍數組中,可見

      • 因此,事務 A 的第二次查詢會讀到事務 B 提交后的數據。

    四、當前讀與一致性讀

    MVCC 的讀操作分為兩種模式:

    1. 一致性讀(Consistent Read):基于視圖讀取歷史版本,用于普通?SELECT

    2. 當前讀(Current Read):讀取最新數據并加鎖,用于更新操作(如?UPDATESELECT ... FOR UPDATE)。

    為什么更新需要當前讀?

    假設事務 B 要更新數據:

    • 若使用一致性讀,可能基于舊版本數據計算新值,導致其他事務的更新丟失。

    • 因此,更新操作必須讀取最新版本(當前讀),并對記錄加鎖,確保數據一致性。

    所以這里可以解釋,為什么事務B執行update操作k是從2變成3,讀到了事務C提交的數據,因為在更新的時候,當前讀拿到的數據是(k=2),更新后生成了新版本的數據(k=3),這個新版本的row trx_id是101。所以,在執行事務B查詢語句的時候,一看自己的版本號是101,最新數據的版本號也是101,是自己的更新,可以直接使用,所以查詢得到的k的值是3。

    五、案例分析

    除了文章開始案例,我們這里再列舉幾個案例分析下。

    案例1

    事務A事務B事務C
    start transaction with consistent snaption
    start transaction with consistent snaption

    start transaction with consistent snaption;

    update t set k=k+1 where id =1;

    update t set k=k+1 where id =1;

    select k from t where id =1;

    select k from t where id =1;

    commit;

    commit
    commit

    我們看上面實例,跟前面分析相比,事務C執行update操作后并沒有立即提交,那么如何執行呢。

    這里我們需要介紹二階段協議:

    ?在InnoDB事務中,行鎖是在需要的時候才加上的,但并不是不需要了就立刻釋放,而是要等到事務結束時才釋放。這個就是兩階段鎖協議。

    執行流程:

    1. 事務C啟動

      • 更新?id=1?的行,將?k?從?1?改為?2,但未提交(持有行鎖)。

      • 此時數據版本鏈為:V1(row trx_id=90, k=1)?→?V2(row trx_id=102, k=2)(未提交)。

    2. 事務B啟動

      • 嘗試執行?UPDATE t SET k=k+1 WHERE id=1,需要獲取行鎖。

      • 因事務C未提交,事務B被阻塞,進入鎖等待狀態

    3. 事務A啟動(RR隔離級別):

      • 執行?SELECT k?FROM t WHERE id=1

      • 根據一致性視圖規則,事務A的視圖數組包含啟動時活躍事務(如事務C的ID=102)。

      • 數據版本鏈中,V2?的?row trx_id=102?在活躍事務數組中,不可見;最終讀取?V1k=1)。

    4. 事務C提交

      • 提交后釋放行鎖,數據版本?V2?的?row trx_id=102?變為已提交。

      • 事務B獲得鎖,執行當前讀,讀取最新版本?V2k=2),更新為?k=3,生成新版本?V3(row trx_id=101)

    5. 事務A再次查詢

      • 仍基于啟動時的視圖,不可見事務B和C'的提交,結果仍為?k=1

    6. 事務B提交

      • 提交后數據版本?V3?的?row trx_id=101?變為已提交。

      • 新事務查詢會看到?k=3

    注意:上面沒有死鎖風險,因為只有事務C和事務B在競爭同一行的鎖,且是單向等待(事務B等待事務C釋放鎖),無循環依賴,因此不會死鎖。

    案例2

    事務A事務B事務C
    start transaction with consistent snaption
    start transaction with consistent snaption

    start transaction with consistent snaption;

    update t set k=k+1 where id =1;

    update t set k=k+1 where id =2;

    select k from t where id =1;

    commit;

    update t set k=k+1 where id =2;

    update t set k=k+1 where id =1;
    commitcommit

    如果出現上面場景呢?

    執行流程

    1. 事務C:更新行id=1?→ 持有行1的鎖。

    2. 事務B:更新行id=2 → 持有行2的鎖。

    3. 事務C:嘗試更新行2 → 等待事務B釋放行2的鎖。

    4. 事務B:嘗試更新行1?→ 等待事務C釋放行1的鎖。

    此時,事務B和事務C互相等待對方釋放資源,形成循環依賴,觸發死鎖。

    死鎖相關分析可以參考:Mysql死鎖_mysql 死鎖的條件-CSDN博客

    案例3

    事務A事務B
    begin
    select k from t
    update t set k=k+1

    update t set k=0 where id = k;

    select k from t;

    上面運行結果:數據庫會拒絕事務A的修改(如報錯或阻塞),“數據無法修改”。

    六、MVCC 的優缺點

    優點

    • 高并發:讀寫不互相阻塞,讀操作無需加鎖。

    • 避免臟讀和不可重復讀:通過版本鏈和可見性規則實現隔離。

    缺點

    • 存儲開銷:需保留多個數據版本和 undo log。

    • 長事務問題:長事務可能導致大量歷史版本無法清理,占用存儲空間。


    六、實際應用建議

    1. 避免長事務:監控?information_schema.innodb_trx,及時終止長時間未提交的事務。

    2. 優先使用 RC 隔離級別:若業務允許,RC 比 RR 更節省資源。

    3. 更新前顯式加鎖:如需確保數據一致性,使用?SELECT ... FOR UPDATE?明確加鎖。

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/diannao/77218.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/diannao/77218.shtml
    英文地址,請注明出處:http://en.pswp.cn/diannao/77218.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    【SpringCloud】從入門到精通【上】

    今天主播我把黑馬新版微服務課程MQ高級之前的內容都看完了&#xff0c;雖然在看視頻的時候也記了筆記&#xff0c;但是看完之后還是忘得差不多了&#xff0c;所以打算寫一篇博客再溫習一下內容。 課程坐標:黑馬程序員SpringCloud微服務開發與實戰 微服務 認識單體架構 單體架…

    力扣hot100_回溯(2)_python版本

    一、39. 組合總和&#xff08;中等&#xff09; 代碼&#xff1a; class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:ans []path []def dfs(i: int, left: int) -> None:if left 0:# 找到一個合法組合ans.append(pa…

    AI平臺如何實現推理?數算島是一個開源的AI平臺(主要用于管理和調度分布式AI訓練和推理任務。)

    數算島是一個開源的AI平臺&#xff0c;主要用于管理和調度分布式AI訓練和推理任務。它基于Kubernetes構建&#xff0c;支持多種深度學習框架&#xff08;如TensorFlow、PyTorch等&#xff09;。以下是數算島實現模型推理的核心原理、架構及具體實現步驟&#xff1a; 一、數算島…

    cesium項目之cesiumlab地形數據加載

    之前的文章我們有提到&#xff0c;使用cesiumlab加載地形出現了一些錯誤&#xff0c;沒有解決&#xff0c;今天作者終于找到了解決方法&#xff0c;下面描述一下具體步驟&#xff0c;首先在地理數據云下載dem數據&#xff0c;在cesiumlab中使用地形切片&#xff0c;得到terrain…

    [Vue]App.vue講解

    頁面中可以看見的內容不再在index.html中進行編輯&#xff0c;而是在App.vue中進行編輯。 組件化開發 在傳統的html開發中&#xff0c;一個頁面的資源往往都寫在同一個html文件中。這種模式在開發小規模、樣式簡單的項目時會相當便捷&#xff0c;但當項目規模越來越大&#xf…

    sql-labs靶場 less-1

    文章目錄 sqli-labs靶場less 1 聯合注入 sqli-labs靶場 每道題都從以下模板講解&#xff0c;并且每個步驟都有圖片&#xff0c;清晰明了&#xff0c;便于復盤。 sql注入的基本步驟 注入點注入類型 字符型&#xff1a;判斷閉合方式 &#xff08;‘、"、’、“”&#xf…

    藍橋杯-小明的彩燈(差分)

    問題描述&#xff1a; 差分數組 1. 什么是差分數組&#xff1f; 差分數組 c 是原數組 a 的“差值表示”&#xff0c;其定義如下&#xff1a; c[0] a[0]c[i] a[i] - a[i-1] &#xff08;i ≥ 1&#xff09; 差分數組記錄了相鄰元素的差值。例如&#xff0c;原數組 a [1, …

    精品可編輯PPT | 基于湖倉一體構建數據中臺架構大數據湖數據倉庫一體化中臺解決方案

    本文介紹了基于湖倉一體構建數據中臺架構的技術創新與實踐。它詳細闡述了數據湖、數據倉庫和數據中臺的概念&#xff0c;分析了三者的區別與協作關系&#xff0c;指出數據湖可存儲大規模結構化和非結構化數據&#xff0c;數據倉庫用于高效存儲和快速查詢以支持決策&#xff0c;…

    最近api.themoviedb.org無法連接的問題解決

    修改NAS的host需要用到SSH終端連接工具&#xff0c;比如常見的Putty&#xff0c;XShell&#xff0c;或者FinalShell等都可以&#xff0c;我個人還是習慣Putty。 1.輸入命令“ sudo -i ”回車&#xff0c;提示輸入密碼&#xff0c;密碼就是我們NAS的登錄密碼&#xff0c;輸入的…

    0.機器學習基礎

    0.人工智能概述&#xff1a; &#xff08;1&#xff09;必備三要素&#xff1a; 數據算法計算力 CPU、GPU、TPUGPU和CPU對比&#xff1a; GPU主要適合計算密集型任務&#xff1b;CPU主要適合I/O密集型任務&#xff1b; 【筆試問題】什么類型程序適合在GPU上運行&#xff1…

    多類型醫療自助終端智能化升級路徑(代碼版.下)

    醫療人機交互層技術實施方案 一、多模態交互體系 1. 醫療語音識別引擎 # 基于Wav2Vec2的醫療ASR系統 from transformers import Wav2Vec2Processor, Wav2Vec2ForCTC import torchaudioclass MedicalASR:def __init__(self):self.processor = Wav2Vec2Processor.from_pretrai…

    前端基礎:React項目打包部署服務器教程

    問題背景 我做了一個React框架的前端的Node項目&#xff0c;是一個單頁面應用。 頁面路由用的是&#xff0c;然后使用了React.lazy在路由層級對每一個不同頁面進行了懶加載&#xff0c;只有打開那個頁面才會加載對應資源。 然后現在我用了Webpack5對項目進行了打包&#xff…

    【深度學習:理論篇】--Pytorch基礎入門

    目錄 1.Pytorch--安裝 2.Pytorch--張量 3.Pytorch--定義 4.Pytorch--運算 4.1.Tensor數據類型 4.2.Tensor創建 4.3.Tensor運算 4.4.Tensor--Numpy轉換 4.5.Tensor--CUDA&#xff08;GPU&#xff09; 5.Pytorch--自動微分 &#xff08;autograd&#xff09; 5.1.back…

    使用 Spring Boot 快速構建企業微信 JS-SDK 權限簽名后端服務

    使用 Spring Boot 快速構建企業微信 JS-SDK 權限簽名后端服務 本篇文章將介紹如何使用 Spring Boot 快速構建一個用于支持企業微信 JS-SDK 權限校驗的后端接口&#xff0c;并提供一個簡單的 HTML 頁面進行功能測試。適用于需要在企業微信網頁端使用掃一掃、定位、錄音等接口的…

    工程師 - FTDI SPI converter

    中國網站&#xff1a;FTDIChip- 首頁 UMFT4222EV-D UMFT4222EV-D - FTDI 可以下載Datasheet。 UMFT4222EVUSB2.0 to QuadSPI/I2C Bridge Development Module Future Technology Devices International Ltd. The UMFT4222EV is a development module which uses FTDI’s FT4222H…

    rcore day6

    批處理系統 (Batch System) 出現于計算資源匱乏的年代&#xff0c;其核心思想是&#xff1a; 將多個程序打包到一起輸入計算機&#xff1b;當一個程序運行結束后&#xff0c;計算機會 自動 執行下一個程序 應用程序難免會出錯&#xff0c;如果一個程序的錯誤導致整個操作系統都…

    Linux系統學習Day2——在Linux系統中開發OpenCV

    一、OpenCV簡介 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一個開源的跨平臺計算機視覺和機器學習庫&#xff0c;廣泛應用于圖像處理、視頻分析、物體檢測等領域。它提供了豐富的算法和高效的工具集&#xff0c;支持C、Python等多種語言&#xff0c…

    SAP Overview

    SAP—企業運營的數字化引擎 在數字化轉型的浪潮中&#xff0c;SAP以其全面的企業應用軟件套件&#xff0c;為全球企業提供了強大的運營支持。SAP的模塊化解決方案覆蓋了企業運作的每一個關鍵環節&#xff0c;從銷售到倉庫管理&#xff0c;每個模塊都是針對特定業務需求精心設計…

    Kafka 中的冪等機制

    Kafka 中的 冪等性&#xff08;Idempotence&#xff09; 是生產者端的重要機制&#xff0c;旨在確保即使在網絡抖動、重試、Broker 重啟等情況下&#xff0c;同一條消息不會被重復寫入到 Topic 中。這是實現可靠消息傳遞、避免重復消費的關鍵手段之一。 ? 什么是冪等性&#…

    用c語言寫一個linux進程之間通信(聊天)的簡單程序

    使用talk 用戶在同一臺機器上talk指令格式如下&#xff1a; ? talk 用戶名ip地址 [用戶終端號] 如果用戶只登錄了一個終端&#xff0c;那么可以不寫用戶終端號&#xff0c;如&#xff1a; talk userlocalhost可以使用who指令來查看當前有哪些用戶登錄&#xff0c;他的終端號…