欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL中的MVCC底層原理解讀

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

簡介

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

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

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

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

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

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

ReadView

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

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

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

版本鏈

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

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

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

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

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

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

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

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

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

演示過程

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

Transaction 100Transaction 200Transaction 300無事務(wù)ID無事務(wù)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;

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

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

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

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

此時的版本鏈和ReadView如下:

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

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

此時的版本鏈和ReadView如下:

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

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

此時的版本鏈和ReadView如下:

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

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

此時的版本鏈和ReadView如下:

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

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

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

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

總結(jié)

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

相關(guān)文章

  • mysql免安裝版的實際配置方法

    mysql免安裝版的實際配置方法

    本文主要向大家講述的是MySQL 免安裝版的實際配置方法,以及對其的相關(guān)的下載網(wǎng)址也有詳細介紹,望你會有所收獲。
    2010-08-08
  • MySQL子查詢中order by不生效問題的解決方法

    MySQL子查詢中order by不生效問題的解決方法

    ORDER BY 語句用于根據(jù)指定的列對結(jié)果集進行排序,在日常工作中經(jīng)常會用到,這篇文章主要給大家介紹了關(guān)于MySQL子查詢中order by不生效問題的解決方法,需要的朋友可以參考下
    2021-07-07
  • 學(xué)習(xí)mysql?如何行轉(zhuǎn)列與列傳行

    學(xué)習(xí)mysql?如何行轉(zhuǎn)列與列傳行

    這篇文章主要介紹了mysql行轉(zhuǎn)列與列傳行的使用方法,幫助大家更好的理解和學(xué)習(xí)MySQL的使用,語句不難,但有一定的知識參考價值,需要的朋友可以參考一下,希望給你的學(xué)習(xí)帶來幫助
    2022-02-02
  • mysql模糊匹配多個值的兩種方法實例

    mysql模糊匹配多個值的兩種方法實例

    我們平時使用msyql需要模糊的匹配字段的時候,我們第一反應(yīng)就是使用like查詢語句來模糊匹配,下面這篇文章主要給大家介紹了關(guān)于mysql模糊匹配多個值的兩種方法,需要的朋友可以參考下
    2022-12-12
  • mysql 的load data infile

    mysql 的load data infile

    前些日子在開發(fā)一個輿情監(jiān)測系統(tǒng),需要在一個操作過程中往數(shù)據(jù)表里插入大量的數(shù)據(jù),為了改變以往生硬地逐條數(shù)據(jù)插入的笨辦法,也為了提高執(zhí)行效率,決定用load data infile來執(zhí)行數(shù)據(jù)插入。
    2009-05-05
  • mysql索引使用技巧及注意事項

    mysql索引使用技巧及注意事項

    本篇文章主要介紹了mysql索引使用技巧及注意事項,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • MySQL優(yōu)化總結(jié)-查詢總條數(shù)

    MySQL優(yōu)化總結(jié)-查詢總條數(shù)

    這篇文章主要介紹了MySQL優(yōu)化總結(jié)-查詢總條數(shù)的相關(guān)內(nèi)容,文中進行簡單的測試對比,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • Mysql雙主搭建的方法步驟

    Mysql雙主搭建的方法步驟

    本文主要介紹了Mysql雙主搭建的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 解決mysql導(dǎo)入還原時亂碼的問題

    解決mysql導(dǎo)入還原時亂碼的問題

    sql文件,直接記事本方式打開,中文顯示正常,還原導(dǎo)入后,發(fā)現(xiàn)中文是亂碼
    2012-12-12
  • 如何開啟mysql中的嚴格模式

    如何開啟mysql中的嚴格模式

    這篇文章介紹了如何開啟mysql中的嚴格模式,有需要的朋友可以參考一下
    2013-09-09

最新評論