圖文結合帶你搞懂InnoDB MVCC

来源:https://www.cnblogs.com/greatsql/archive/2022/10/25/16824493.html
-Advertisement-
Play Games

GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 前情提要 當前讀 快照讀 什麼是MVCC 三個隱藏欄位 Undo Log回滾日誌 MVCC版本鏈 ReadView讀視圖 不同隔離級別下MVCC分析 R ...


  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。
  • GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。

    • 前情提要
    • 當前讀
    • 快照讀
  • 什麼是MVCC
    • 三個隱藏欄位
    • Undo Log回滾日誌
    • MVCC版本鏈
    • ReadView讀視圖
  • 不同隔離級別下MVCC分析
    • READ-COMMITTED隔離級別
    • REPEATABLE-READ隔離級別

前情提要

事務有四大特性ACID分別是:原子性(Atomicity)一致性(Consistency)隔離性(Isolation)持久性(Durability)

其中隔離性是通過資料庫的加上MVCC(多版本併發控制)來保證的。

在介紹MVCC之前先來瞭解一下當前讀和快照讀。

當前讀

當前讀讀取的是記錄的最新版本。同時在讀取的時候還要保證其他的併發事務不能更改當前記錄,那麼當前讀會對它要讀取的記錄進行加鎖。不同的操作會加上不同類型的鎖,如:SELECT ... LOCK IN SHARE MODE(共用鎖)SELECT ... FOR UPDATE、UPDATE、INSERT、 DELETE(排他鎖)

快照讀

簡單的不加鎖的SELECT就是快照讀,快照讀讀取的是快照生成時的數據,不一定是最新的數據,它是不加鎖的非阻塞讀。而不同隔離級別下,創建快照的時機也不同:

  • READ-COMMITTED(讀已提交):事務每次SELECT時創建ReadView
  • REPEATABLE-READ(可重覆讀):事務第一次SELECT時創建ReadView,後續一直使用

在MySQL預設隔離級別(REPEATABLE-READ)下,快照讀保證了數據的可重覆讀。

什麼是MVCC

MVCC全稱Multi-Version Concurrency Control,即多版本併發控制。它是一種併發控制的方法,它可以維護一個數據的多個版本,用更好的方式去處理讀寫衝突,做到即使有讀寫衝突也能不加鎖。MySQL中MVCC的具體實現,還需要依賴於表中的三個隱藏欄位Undo Log日誌以及ReadView

三個隱藏欄位

mysql> SHOW CREATE TABLE stu \G;
*************************** 1. row ***************************
       Table: stu
Create Table: CREATE TABLE `stu` (
  `id` int NOT NULL,
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql> SELECT * FROM stu;
+----+--------+
| id | name   |
+----+--------+
|  1 |   m    |
|  2 |   f    |
+----+--------+

當創建了上述這張表後,我們在查看表結構時只能看到id、name欄位,實際上除了這兩個欄位外,InnoDB引擎還自動為我們添加了三個隱藏欄位,見下表:

欄位 含義
DB_TRX_ID 最近修改事務ID,記錄插入這條記錄或最後一次修改該記錄的事務ID。
DB_ROLL_PTR 回滾指針,指向這條記錄的上一個版本,用於配合Undo Log,指向上一個版本。
DB_ROW_ID 隱藏主鍵,如果表結構沒有指定主鍵,將會生成該隱藏欄位。

我們可以使用ibd2sdi工具來從表空間文件中提取序列化的字典信息(SDI),來驗證一下這三個隱藏欄位是否存在。

["ibd2sdi"
,
{
	"type": 1,
	"id": 402,
	"object":
		{
    "mysqld_version_id": 80025,
    "dd_version": 80023,
    "sdi_version": 80019,
    "dd_object_type": "Table",
    "dd_object": {
        "name": "stu",
        "mysql_version_id": 80025,
        "created": 20220919023413,
        "last_altered": 20220919023413,
        "hidden": 1,
        "options": "avg_row_length=0;encrypt_type=N;explicit_encryption=0;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0;",
        "columns": [
            {
                "name": "id",
                "type": 4,
                "is_nullable": false,
                "is_zerofill": false,
                "is_unsigned": false,
                "is_auto_increment": false,
                "is_virtual": false,
                "hidden": 1,
···省略
            },
            {
                "name": "name",
                "type": 16,
                "is_nullable": false,
                "is_zerofill": false,
                "is_unsigned": false,
                "is_auto_increment": false,
                "is_virtual": false,
                "hidden": 1,
···省略
            },
            {
                "name": "DB_TRX_ID", #最近修改事務ID
                "type": 10,
                "is_nullable": false,
                "is_zerofill": false,
                "is_unsigned": false,
                "is_auto_increment": false,
                "is_virtual": false,
                "hidden": 2,
···省略
            },
            {
                "name": "DB_ROLL_PTR", #回滾指針
                "type": 9,
                "is_nullable": false,
                "is_zerofill": false,
                "is_unsigned": false,
                "is_auto_increment": false,
                "is_virtual": false,
                "hidden": 2,
···省略
            }
        ],

註意:因為這張表裡已經指定了主鍵為id列,所以不會生成隱藏主鍵DB_ROW_ID列。

Undo Log回滾日誌

回滾日誌,在增、改、刪操作的時候產生的便於數據回滾的日誌。當INSERT操作的時候,產生的回滾日誌在事務提交後可被立即刪除。而UPDATEDELETE操作的時候,產生的Undo Log日誌不僅在進行數據回滾時需要,在進行快照讀時也需要,所以不會立即被刪除

Undo Log詳情可見文章:待浩源Undo Log文章發表後添加

MVCC版本鏈

當有多個併發事務操作一行數據時,對這行數據的修改會產生多個版本,多個版本通過上述的一個隱藏欄位DB_ROLL_PTR回滾指針指向Undo Log數據地址形成一個鏈表,即MVCC版本鏈

ReadView讀視圖

ReadView讀視圖是快照讀SQL執行時MVCC提取數據的依據,記錄並維護系統當前活躍的事務(未提交的)id。

上面講過Undo Log和MVCC版本鏈,一條數據經過多次修改會產生多個版本,而快照讀是根據不同時機創建的快照獲取數據的,那麼快照讀SQL在執行時該讀取那個版本的數據就是靠ReadViw讀視圖來決定的。

ReadView讀視圖中包含了四個核心欄位,也是讀取數據的判斷依據:

欄位 含義
m_ids 當前活躍的事務ID集合
min_trx_id 最小活躍事務ID
max_trx_id 預分配事務ID,當前最大事務ID+1(因為事務ID是自增的)
creator_trx_id ReadView創建者的事務ID

ReadView一共有四種匹配規則:

條件 能否訪問 說明
trx_id == creatro_trx_id 可以訪問該版本 成立,說明數據是當前這個事務更改的。
trx_id < min_trx_id 可以訪問該版本 成立,說明數據已經提交了。
trx_id > max_trx_id 不可以訪問該版本 成立,說明該事務是在ReadView生成後才開啟的。
min_trx_id <= trx_id <= max_trx_id 如果trx_id不在m_ids中,那麼可以訪問該版本 成立,說明數據已經提交。

不同隔離級別下MVCC分析

READ-COMMITTED隔離級別

前面有提到過在READ-COMMITTED隔離級別下事務在每次快照讀SQL執行時創建ReadView,每次創建的ReadView的四個欄位對應的值也是不同的,所以在READ-COMMITTED隔離級別下每次快照讀SQL獲取的數據可能也是不同的。

下麵通過一個READ-COMMITTED隔離級別下併發事務的案例來詳細看看:

現有四個併發事務同時訪問一條數據:

在上述併發事務中,事務5查詢了兩次id為1的數據,因為當前的隔離級別設置為了READ-COMMITTED,事務在每次快照讀SQL執行時創建一個ReadView,每次生成的ReadView中的四個欄位值都不同。那麼三次快照讀都會根據生成的ReadView中的欄位進行規則匹配,從而決定返回的數據。接下來看看流程:

事務5第一次快照讀解讀

事務5第一次進行查詢時生成的ReadView以及原數據如下圖:

在匹配版本數據前,先與表中數據進行匹配:

該數據對應的DB_TRX_ID為3,此時MVCC就會通過ReadView帶著這條數據去進行規則匹配:

首先是第一條規則db_trx_id == creator_trx_id,db_trx_id(3)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id < min_trx_id,db_trx_id(3)不小於min_trx_id(3)故不成立;

第三條規則db_trx_id > max_trx_id,db_trx_id(3)小於max_trx_id(6)故不成立;

第四條規則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(3)在min_trx(3)與max_trx_id(6)之間,但是同時處於m_ids(3,4,5)集合之中故也不成立。

經過這次匹配,表中最新的數據無法匹配,故要與MVCC版本鏈中最上面的數據進行規則匹配

與MVCC版本鏈中最上方的版本進行匹配:

第一條規則db_trx_id(2)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id(2)小於min_trx_id(3),該版本的數據滿足匹配規則中的第二條,說明數據已經提交,此時匹配將終止並返回這個版本對應的數據。

事務5第二次快照讀

因為當前事務的隔離級別為READ-COMMITTED(讀已提交),所以在每次快照讀的時候都會創建一個ReadView,所以事務5第二次進行查詢時生成的ReadView以及原數據如下圖:

在匹配版本數據前,先與表中數據進行匹配:

該數據對應的DB_TRX_ID為4,此時MVCC就會通過ReadView帶著這條數據去進行規則匹配:

首先是第一條規則db_trx_id == creator_trx_id,db_trx_id(4)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id < min_trx_id,db_trx_id(4)不小於min_trx_id(4)故不成立;

第三條規則db_trx_id > max_trx_id,db_trx_id(4)小於max_trx_id(6)故不成立;

第四條規則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(4)在min_trx(4)與max_trx_id(6)之間,但是同時處於m_ids(4,5)集合之中故也不成立。

經過這次匹配,表中最新的數據無法匹配,故要與MVCC版本鏈中最上面的數據進行規則匹配

與MVCC版本鏈中最上方的版本進行匹配:

第一條規則db_trx_id(3)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id(3)小於min_trx_id(4),該版本的數據滿足匹配規則中的第二條,說明數據已經提交,此時匹配將終止並返回這個版本對應的數據。

REPEATABLE-READ級別

現在來看看REPEATABLE-READ可重覆讀隔離級別有什麼不同的地方。同樣,有四個併發事務同時訪問一條數據:

在上述併發事務中,事務5查詢了兩次id為1的數據,因為當前的隔離級別設置為了REPEATABLE-READ,事務在第一次快照讀SQL執行時創建ReadView,後續該事務所有的快照讀都復用該ReadView。接下來看看流程:

事務5第一次快照讀解讀

事務5第一次進行查詢時生成的ReadView以及原數據如下圖:

在匹配版本數據前,先與表中數據進行匹配:

該數據對應的DB_TRX_ID為3,此時MVCC就會通過ReadView帶著這條數據去進行規則匹配:

首先是第一條規則db_trx_id == creator_trx_id,db_trx_id(3)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id < min_trx_id,db_trx_id(3)不小於min_trx_id(3)故不成立;

第三條規則db_trx_id > max_trx_id,db_trx_id(3)小於max_trx_id(6)故不成立;

第四條規則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(3)在min_trx(3)與max_trx_id(6)之間,但是同時處於m_ids(3,4,5)集合之中故也不成立。

經過這次匹配,表中最新的數據無法匹配,故要與MVCC版本鏈中最上面的數據進行規則匹配

與MVCC版本鏈中最上方的版本進行匹配:

第一條規則db_trx_id(2)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id(2)小於min_trx_id(3),該版本的數據滿足匹配規則中的第二條,說明數據已經提交,此時匹配將終止並返回這個版本對應的數據。

事務5第二次快照讀解讀

因為當前事務的隔離級別為REPEATABLE-READ(可重覆讀),所以第二次快照讀也會沿用第一次快照讀時創建的ReadView,如下:

在匹配版本數據前,先與表中數據進行匹配:

該數據對應的DB_TRX_ID為4,此時MVCC就會通過ReadView帶著這條數據去進行規則匹配:

首先是第一條規則db_trx_id == creator_trx_id,db_trx_id(4)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id < min_trx_id,db_trx_id(4)不小於min_trx_id(3)故不成立;

第三條規則db_trx_id > max_trx_id,db_trx_id(4)小於max_trx_id(6)故不成立;

第四條規則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(4)在min_trx(4)與max_trx_id(6)之間,但是同時處於m_ids(4,5)集合之中故也不成立。

經過這次匹配,表中最新的數據無法匹配,故要與MVCC版本鏈中最上面的數據進行規則匹配

與MVCC版本鏈中最上方的版本進行匹配:

第一條規則db_trx_id(3)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id(3)不小於min_trx_id(4)故不成立;

第三條規則db_trx_id小於max_trx_id(6)故不成立;

第四條規則db_trx_id(3)在min_trx(3)與max_trx_id(6)之間,但是同時處於m_ids(3,4,5)集合之中故也不成立。

經過第二次匹配,MVCC版本鏈中最上層的數據版本也無法匹配,故要與第二條版本進行匹配

與MVCC版本鏈中第二條版本進行匹配:

第一條規則db_trx_id(2)不等於creator_trx_id(5)故不成立;

第二條規則db_trx_id(2)小於min_trx_id(3),該版本的數據滿足匹配規則中的第二條,說明數據已經提交,此時匹配將終止並返回這個版本對應的數據。

Enjoy GreatSQL

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前嘗試用swarm部署redis集群時網上看了很多帖子,發現大多數都是單機集群,也就是在一個伺服器上啟多個redis容器,然後進入其中一個容器執行redis搭建,經過研究,我實現了只需要通過docker-compose.yml文件和一個啟動命令就完成redis分散式部署的方式,讓其分別部署在不同機 ...
  • LVS: lvs是一個負載均衡的一個集群軟體,由內核集成,性能強大,支持百萬計併發。 LVS集群的相關概念: VS:虛擬伺服器,指LVS伺服器自身 RS:提供服務的伺服器 CIP:客戶端ip地址 VIP:lvs伺服器對外發佈的ip地址,用戶通過vip訪問集群 DIP:LVS連內網的ip地址叫DIP, ...
  • 記錄一下 Macbook 本地折騰 Wordpress 的完整過程 第一步 安裝 MySQL 詳見上一篇筆記 MacOS 安裝 MySQL 與配置環境變數 第二步 新建資料庫、用戶、分配許可權 mysql> create database 資料庫名; //註意SQL命令結尾要帶分號 mysql> SE ...
  • 寫在本文開始之前.... 從本文開始我們就正式開啟了 Linux 內核記憶體管理子系統源碼解析系列,筆者還是會秉承之前系列文章的風格,採用一步一圖的方式先是詳細介紹相關原理,在保證大家清晰理解原理的基礎上,我們再來一步一步的解析相關內核源碼的實現。有了源碼的輔證,這樣大家看得也安心,理解起來也放心,最 ...
  • sersync: sersync類似於inotify,同樣用於監控,但它剋服了inotify的缺點. inotify+rsync 缺點: 一個操作可能會產生重覆的事件,這樣可能會觸發rsync的多次同步。 serync特點: 基於inotify基礎上開發而來的。 sersync的作用: 用來替代in ...
  • 文件定時同步的實現: 利用rsync結合cron計劃任務實現: rsync -av --delete /data/ 10.0.0.12:/back -a:保留文件屬性 -v:顯示過程 -delete:如果源文件沒有的,目標文件裡面有,就把目標文件裡面的刪除掉 文件實時同步的實現: 前提: 文件發生髮 ...
  • 本文主要描述企業用戶如何從阿裡雲市場購買、使用石原子公司的AtomData企業級實時數倉產品。 1、購買 1.1 找到商品 通過阿裡雲官網的雲市場類目進入雲市場首頁,搜索AtomData,即可發現產品,如下圖。 1.2 選擇規格 點擊雲市場的商品,進入AtomData商品詳情頁,在商品詳情頁可以選擇 ...
  • 本文主要描述用戶如何從阿裡雲市場購買、使用石原子公司的StoneDB(HTAP)資料庫產品。 1、購買 1.1 找到商品 通過阿裡雲官網的雲市場類目進入雲市場首頁,搜索StoneDB,即可發現產品,如下圖: 1.2 選擇規格 點擊雲市場的商品,進入StoneDB商品詳情頁,在商品詳情頁可以選擇需要購 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...