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

MySQL中的MVCC底層原理解讀

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

簡(jiǎn)介

MVCC(Multi-Version Concurrency Control)多版本并發(fā)控制,是用來(lái)在數(shù)據(jù)庫(kù)中控制并發(fā)的方法,實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的并發(fā)訪(fǎng)問(wèn)用的。

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

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

首先需要知道的是,在MySQL中,會(huì)默認(rèn)為我們的表后面添加三個(gè)隱藏字段:

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

(注意,添加的隱藏字段并不是很多人認(rèn)為的創(chuàng)建時(shí)間和刪除時(shí)間,同時(shí)在MySQL中MVCC的實(shí)現(xiàn)也不是通過(guò)什么快照來(lái)實(shí)現(xiàn)的。之所以有這種說(shuō)法可能是源自于《高性能MySQL》一書(shū)中對(duì)MySQL中MVCC的錯(cuò)誤結(jié)論,然后就人云亦云傳開(kāi)了(注意,我這里一直強(qiáng)調(diào)的是MySQL中MVCC的實(shí)現(xiàn),是因?yàn)樵诓煌臄?shù)據(jù)庫(kù)中可能會(huì)有不同的實(shí)現(xiàn))。所以說(shuō)看源碼和看官方文檔才是最權(quán)威的解釋?zhuān)?/p>

ReadView

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

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

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

版本鏈

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

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

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

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

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

如果落在紅色區(qū)間(DB_TRX_ID > max_id):表示這個(gè)版本是由將來(lái)啟動(dòng)的事務(wù)來(lái)生成的,當(dāng)前還未開(kāi)始,那么是不可見(jiàn)的;

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

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

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

演示過(guò)程

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

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

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

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

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

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

此時(shí)的版本鏈和ReadView如下:

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

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

此時(shí)的版本鏈和ReadView如下:

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

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

此時(shí)的版本鏈和ReadView如下:

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

同時(shí)在第12時(shí)刻點(diǎn),第五個(gè)事務(wù)執(zhí)行了一條SELECT語(yǔ)句,想要查詢(xún)一下當(dāng)前id為1的數(shù)據(jù)。

此時(shí)的版本鏈和ReadView如下:

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

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

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

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

總結(jié)

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

相關(guān)文章

  • mysql免安裝版的實(shí)際配置方法

    mysql免安裝版的實(shí)際配置方法

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

    MySQL子查詢(xún)中order by不生效問(wèn)題的解決方法

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

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

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

    mysql模糊匹配多個(gè)值的兩種方法實(shí)例

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

    mysql 的load data infile

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

    mysql索引使用技巧及注意事項(xiàng)

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

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

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

    Mysql雙主搭建的方法步驟

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

    解決mysql導(dǎo)入還原時(shí)亂碼的問(wèn)題

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

    如何開(kāi)啟mysql中的嚴(yán)格模式

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

最新評(píng)論