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

MySQL中的MVCC底層原理解讀

 更新時間:2025年02月07日 16:43:44   作者:天瑕  
本文詳細介紹了MySQL中的多版本并發(fā)控制(MVCC)機制,包括版本鏈、ReadView以及在不同事務隔離級別下MVCC的工作原理,通過一個具體的示例演示了在可重復讀隔離級別下的MVCC執(zhí)行過程

簡介

MVCC(Multi-Version Concurrency Control)多版本并發(fā)控制,是用來在數(shù)據(jù)庫中控制并發(fā)的方法,實現(xiàn)對數(shù)據(jù)庫的并發(fā)訪問用的。

在MySQL中,MVCC只在讀取已提交(Read Committed)可重復讀(Repeatable Read)兩個事務級別下有效。其是通過Undo日志中的版本鏈ReadView一致性視圖來實現(xiàn)的。

MVCC就是在多個事務同時存在時,SELECT語句找尋到具體是版本鏈上的哪個版本,然后在找到的版本上返回其中所記錄的數(shù)據(jù)的過程。

首先需要知道的是,在MySQL中,會默認為我們的表后面添加三個隱藏字段:

  • DB_ROW_ID:行ID,MySQL的B+樹索引特性要求每個表必須要有一個主鍵。如果沒有設置的話,會自動尋找第一個不包含NULL的唯一索引列作為主鍵。如果還是找不到,就會在這個DB_ROW_ID上自動生成一個唯一值,以此來當作主鍵(該列和MVCC的關系不大);
  • DB_TRX_ID:事務ID,記錄的是當前事務在做INSERT或UPDATE語句操作時的事務ID(DELETE語句被當做是UPDATE語句的特殊情況,后面會進行說明);
  • DB_ROLL_PTR:回滾指針,通過它可以將不同的版本串聯(lián)起來,形成版本鏈。相當于鏈表的next指針。

(注意,添加的隱藏字段并不是很多人認為的創(chuàng)建時間和刪除時間,同時在MySQL中MVCC的實現(xiàn)也不是通過什么快照來實現(xiàn)的。之所以有這種說法可能是源自于《高性能MySQL》一書中對MySQL中MVCC的錯誤結論,然后就人云亦云傳開了(注意,我這里一直強調(diào)的是MySQL中MVCC的實現(xiàn),是因為在不同的數(shù)據(jù)庫中可能會有不同的實現(xiàn))。所以說看源碼和看官方文檔才是最權威的解釋)

ReadView

ReadView一致性視圖主要是由兩部分組成:所有未提交事務的ID數(shù)組已經(jīng)創(chuàng)建的最大事務ID組成(實際上ReadView還有其他的字段,但不影響這里對MVCC的講解)。

比如:[100,200],300。事務100和200是當前未提交的事務,而事務300是當前創(chuàng)建的最大事務(已經(jīng)提交了)。

當執(zhí)行SELECT語句的時候會創(chuàng)建ReadView,但是在讀取已提交和可重復讀兩個事務級別下,生成ReadView的策略是不一樣的:讀取已提交級別是每執(zhí)行一次SELECT語句就會重新生成一份ReadView,而可重復讀級別是只會在第一次SELECT語句執(zhí)行的時候會生成一份,后續(xù)的SELECT語句會沿用之前生成的ReadView(即使后面有更新語句的話,也會繼續(xù)沿用)。

版本鏈

所有版本的數(shù)據(jù)都只會存一份,然后通過回滾指針連接起來,之后就是通過一定的規(guī)則找到具體是哪個版本上的數(shù)據(jù)就行了。

假設現(xiàn)在有一張account表,其中有id和name兩個字段,那么版本鏈的示意圖如下:

而具體版本鏈的比對規(guī)則如下,首先從版本鏈中拿出最上面第一個版本的事務ID開始逐個往下進行比對:

(其中min_id指向ReadView中未提交事務數(shù)組中的最小事務ID,而max_id指向ReadView中的已經(jīng)創(chuàng)建的最大事務ID)

如果落在綠色區(qū)間(DB_TRX_ID < min_id):這個版本比min_id還?。ㄊ聞誌D是從小往大順序生成的),說明這個版本在SELECT之前就已經(jīng)提交了,所以這個數(shù)據(jù)是可見的?;蛘撸ㄟ@里是短路或,前面條件不滿足才會判斷后面這個條件)這個版本的事務本身就是當前SELECT語句所在事務的話,也是一樣可見的;

如果落在紅色區(qū)間(DB_TRX_ID > max_id):表示這個版本是由將來啟動的事務來生成的,當前還未開始,那么是不可見的;

如果落在黃色區(qū)間(min_id <= DB_TRX_ID <= max_id):這個時候就需要再判斷兩種情況:

  • 如果這個版本的事務ID在ReadView的未提交事務數(shù)組中,表示這個版本是由還未提交的事務生成的,那么就是不可見的;
  • 如果這個版本的事務ID不在ReadView的未提交事務數(shù)組中,表示這個版本是已經(jīng)提交了的事務生成的,那么是可見的。

如果在上述的判斷中發(fā)現(xiàn)當前版本是不可見的,那么就繼續(xù)從版本鏈中通過回滾指針拿取下一個版本來進行上述的判斷。

演示過程

下面通過一個示例來具體演示MVCC的執(zhí)行過程(假設是在可重復讀事務級別下),當前account表中已經(jīng)有了一條初始數(shù)據(jù)(id=1,name=monkey):

Transaction 100Transaction 200Transaction 300無事務ID無事務ID
1begin;begin;begin;begin;begin;
2UPDATE test SET a='1' WHERE id = 1;
3UPDATE test SET a='2' WHERE id = 2;
4UPDATE account SET name = 'monkey301' WHERE id = 1;
5commit;
6SELECT name FROM account WHERE id = 1;
7UPDATE account SET name = 'monkey101' WHERE id = 1;
8UPDATE account SET name = 'monkey102' WHERE id = 1;
9SELECT name FROM account WHERE id = 1;
10commit;UPDATE account SET name = 'monkey201' WHERE id = 1;
11UPDATE account SET name = 'monkey202' WHERE id = 1;
12SELECT name FROM account WHERE id = 1;SELECT name FROM account WHERE id = 1;
13commit;

從左往右分別是五個事務,從上到下是時刻點。其中在第2和3時刻點中事務100和事務200(這里兩個事務之間相差100只是為了更加方便去看,正常來說下個事務的ID是以+1的方式來創(chuàng)建的)分別執(zhí)行了一條UPDATE語句,這兩條語句并無實際作用,只是為了生成事務ID的,所以在下面的MVCC執(zhí)行過程中就不分析這兩條語句所帶來的影響了,我們只研究account表。而其中最后兩個事務,我是注明沒有事務ID的。因為事務ID是執(zhí)行一條更新操作(增刪改)的語句后才會生成(這也是事務100和事務200要先執(zhí)行一條更新語句的意義),并不是開啟事務的時候就會生成。最后兩個事務中可以看到就是執(zhí)行了一些SELECT語句而已,所以它們并沒有事務ID。

首先來看一下初始狀態(tài)時的版本鏈和ReadView(ReadView此時還未生成):

其中事務1在account表中創(chuàng)建了一條初始數(shù)據(jù)。

  • 之后在第1時刻點,五個事務分別開啟了事務(如上所說,這個時候還沒有生成事務ID)。
  • 在第2時刻點,第一個事務執(zhí)行了一條UPDATE語句,生成了事務ID為100。
  • 在第3時刻點,第二個事務執(zhí)行了一條UPDATE語句,生成了事務ID為200。
  • 在第4時刻點,第三個事務執(zhí)行了一條UPDATE語句,將account表中id為1的name改為了monkey301。同時生成了事務ID為300。
  • 在第5時刻點,事務300也就是上面的事務執(zhí)行了commit操作。
  • 在第6時刻點,第四個事務執(zhí)行了一條SELECT語句,想要查詢一下當前id為1的數(shù)據(jù)(如上所說,該事務沒有生成事務ID)。

此時的版本鏈和ReadView如下:

因為在第5時刻點,事務300已經(jīng)commit了,所以ReadView的未提交事務數(shù)組中不包含它。此時根據(jù)上面所說的比對規(guī)則,拿版本鏈中的第一個版本的事務ID為300進行比對,首先當前這條SELECT語句沒有在事務300中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間,而且事務300也沒有在ReadView的未提交事務數(shù)組中,所以是可見的。即此時在第6時刻點,第四個事務所查找到的結果是monkey301。

  • 在第7時刻點,事務100執(zhí)行了一條UPDATE語句,將account表中id為1的name改為了monkey101。
  • 在第8時刻點,事務100又執(zhí)行了一條UPDATE語句,將account表中id為1的name改為了monkey102。
  • 在第9時刻點,第四個事務執(zhí)行了一條SELECT語句,想要查詢一下當前id為1的數(shù)據(jù)。

此時的版本鏈和ReadView如下:

注意,因為當前是在可重復讀的事務級別下,所以此時的ReadView沿用了在第6時刻點生成的ReadView(如果是在讀取已提交的事務級別下,此時就會重新生成一份ReadView了)。然后根據(jù)上面所說的比對規(guī)則,拿版本鏈中的第一個版本的事務ID為100進行比對,首先當前這條SELECT語句沒有在事務100中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間,而且事務100是在ReadView的未提交事務數(shù)組中,所以是不可見的。此時通過回滾指針拿取下一個版本,發(fā)現(xiàn)事務ID仍然為100,經(jīng)過分析后還是不可見的。此時又拿取下一個版本:事務ID為300進行比對,首先當前這條SELECT語句沒有在事務300中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間,但是事務300沒有在ReadView的未提交事務數(shù)組中,所以是可見的。即此時在第9時刻點,第四個事務所查找到的結果仍然是monkey301(這也就是可重復讀的含義)。

  • 在第10時刻點,事務100commit提交事務了。同時事務200執(zhí)行了一條UPDATE語句,將account表中id為1的name改為了monkey201。
  • 在第11時刻點,事務200又執(zhí)行了一條UPDATE語句,將account表中id為1的name改為了monkey202。
  • 在第12時刻點,第四個事務執(zhí)行了一條SELECT語句,想要查詢一下當前id為1的數(shù)據(jù)。

此時的版本鏈和ReadView如下:

跟第9時刻點一樣,在可重復讀的事務級別下,ReadView沿用了在第6時刻點生成的ReadView。然后根據(jù)上面所說的比對規(guī)則,拿版本鏈中的第一個版本的事務ID為200進行比對,首先當前這條SELECT語句沒有在事務200中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間,而且事務200是在ReadView的未提交事務數(shù)組中,所以是不可見的。此時通過回滾指針拿取下一個版本,發(fā)現(xiàn)事務ID仍然為200,經(jīng)過分析后還是不可見的。此時又拿取下一個版本:事務ID為100進行比對,首先當前這條SELECT語句沒有在事務100中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間內(nèi),同時在ReadView的未提交數(shù)組中,所以依然是不可見的。此時又拿取下一個版本,發(fā)現(xiàn)事務ID仍然為100,經(jīng)過分析后還是不可見的。此時再拿取下一個版本:事務ID為300進行比對,首先當前這條SELECT語句沒有在事務300中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間,但是事務300沒有在ReadView的未提交事務數(shù)組中,所以是可見的。即此時在第12時刻點,第四個事務所查找到的結果仍然是monkey301。

同時在第12時刻點,第五個事務執(zhí)行了一條SELECT語句,想要查詢一下當前id為1的數(shù)據(jù)。

此時的版本鏈和ReadView如下:

注意,此時第五個事務因為是該事務內(nèi)的第一條SELECT語句,所以會重新生成在當前情況下的ReadView,即上圖中所示的內(nèi)容??梢钥吹剑偷谒膫€事務生成的ReadView并不一樣,因為在之前的第10時刻點,事務100已經(jīng)提交事務了。然后根據(jù)上面所說的比對規(guī)則,拿版本鏈中的第一個版本的事務ID為200進行比對,首先當前這條SELECT語句沒有在事務200中進行查詢,然后發(fā)現(xiàn)是落在黃色區(qū)間,而且事務200是在ReadView的未提交事務數(shù)組中,所以是不可見的。此時通過回滾指針拿取下一個版本,發(fā)現(xiàn)事務ID仍然為200,經(jīng)過分析后還是不可見的。此時又拿取下一個版本:事務ID為100進行比對,發(fā)現(xiàn)是在綠色區(qū)間,所以是可見的。即此時在第12時刻點,第五個事務所查找到的結果是monkey102(可以看到,即使是同一條SELECT語句,在不同的事務中,查詢出來的結果也可能是不同的,究其原因就是因為ReadView的不同)。

  • 在第13時刻點,事務200執(zhí)行了commit操作,整段分析過程結束。

以上演示的就是MVCC的具體執(zhí)行過程,在多個事務下,版本鏈和ReadView是如何配合進行查找的。上面還遺漏了一種情況沒有進行說明,就是如果是DELETE語句的話,也會在版本鏈上將最新的數(shù)據(jù)插入一份,然后將事務ID賦值為當前進行刪除操作的事務ID。但是同時會在該條記錄的信息頭(record header)里面的deleted_flag標記位置為true,以此來表示當前記錄已經(jīng)被刪除。所以如果經(jīng)過版本比對后發(fā)現(xiàn)找到的版本上的deleted_flag標記位為true的話,那么也不會返回,而是繼續(xù)尋找下一個。

另外,如果當前事務執(zhí)行rollback回滾的話,會把版本鏈中屬于該事務的所有版本都刪除掉。

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • mysql基礎:mysqld_safe 啟動執(zhí)行流程詳解

    mysql基礎:mysqld_safe 啟動執(zhí)行流程詳解

    本篇文章是對mysql基礎中的mysqld_safe啟動執(zhí)行流程進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • Qt如何編譯MySQL數(shù)據(jù)庫驅(qū)動

    Qt如何編譯MySQL數(shù)據(jù)庫驅(qū)動

    這篇文章主要介紹了Qt如何編譯MySQL數(shù)據(jù)庫驅(qū)動,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-12-12
  • MySQL由淺入深探究存儲過程

    MySQL由淺入深探究存儲過程

    這篇文章主要介紹了MySQL存儲過程,存儲過程,也叫做存儲程序,是一條或者多條SQL語句的集合,可以視為批量處理,但是其作用不僅僅局限于批量處理
    2022-11-11
  • 基于python的mysql復制工具詳解

    基于python的mysql復制工具詳解

    python-mysql-replication 是基于python實現(xiàn)的 MySQL復制協(xié)議工具,我們可以用它來解析binlog 獲取日志的insert,update,delete等事件 ,并基于此做其他業(yè)務需求。這篇文章主要介紹了基于python的mysql復制工具,需要的朋友可以參考下
    2019-06-06
  • MySQL中sum函數(shù)使用的實例教程

    MySQL中sum函數(shù)使用的實例教程

    這篇文章主要給大家介紹了關于MySQL中sum函數(shù)使用的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • MySQL Cluster如何創(chuàng)建磁盤表方法解讀

    MySQL Cluster如何創(chuàng)建磁盤表方法解讀

    MySQL Cluster采用一系列的Disk Data objects來實現(xiàn)磁盤表;接下來為您詳細介紹
    2012-11-11
  • MySQL數(shù)據(jù)庫遷移OpenGauss數(shù)據(jù)庫解析

    MySQL數(shù)據(jù)庫遷移OpenGauss數(shù)據(jù)庫解析

    這篇文章主要介紹了MySQL數(shù)據(jù)庫遷移OpenGauss數(shù)據(jù)庫解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Mysql占用過高CPU時的優(yōu)化手段(必看)

    Mysql占用過高CPU時的優(yōu)化手段(必看)

    下面小編就為大家?guī)硪黄狹ysql占用過高CPU時的優(yōu)化手段(必看)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Navicat Premiun遠程連接MySQL報錯10038解決方案

    Navicat Premiun遠程連接MySQL報錯10038解決方案

    這篇文章主要介紹了Navicat Premiun遠程連接MySQL報錯10038解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • 實操MySQL+PostgreSQL批量插入更新insertOrUpdate

    實操MySQL+PostgreSQL批量插入更新insertOrUpdate

    這篇文章主要介紹了MYsql和PostgreSQL優(yōu)勢對比以及如何實現(xiàn)MySQL + PostgreSQL批量插入更新insertOrUpdate,附含詳細的InserOrupdate代碼實例,需要的朋友可以參考下
    2021-08-08

最新評論

罗田县| 沁水县| 甘孜| 革吉县| 贡觉县| 绥棱县| 米易县| 慈溪市| 且末县| 桦甸市| 常山县| 德清县| 即墨市| 上蔡县| 勐海县| 旬邑县| 通渭县| 靖远县| 永嘉县| 屏边| 普陀区| 昭通市| 玉树县| 屏东县| 安图县| 兴和县| 静乐县| 通辽市| 苗栗县| 洪湖市| 龙山县| 慈溪市| 彰化市| 阳信县| 仙桃市| 随州市| 佛学| 金沙县| 乾安县| 申扎县| 清徐县|