MySql事務(wù)實(shí)現(xiàn)的機(jī)制:MVCC

MySql事務(wù)實(shí)現(xiàn)的機(jī)制:MVCC

這一篇將簡(jiǎn)單說(shuō)明一下最近學(xué)習(xí)了Mysql的事務(wù)實(shí)現(xiàn)的簡(jiǎn)單理解。

如果存在一張A表,id=1,name=2,此時(shí),存在多個(gè)事務(wù)對(duì)該表進(jìn)行處理,那么會(huì)怎樣呢?

假設(shè)我們會(huì)存在多個(gè)事務(wù)同時(shí)進(jìn)行,

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1
7
8
9

首先運(yùn)行第2行sql

當(dāng)程序運(yùn)行到第2行的時(shí)候,由于事務(wù)1對(duì)A表進(jìn)行了更新操作,此時(shí)會(huì)產(chǎn)生一個(gè)事務(wù)id,我們假設(shè)該id的值為2。其中事務(wù)id是一個(gè)自增長(zhǎng)的id,僅當(dāng)事務(wù)中進(jìn)行了更新操作的時(shí)候生成,同時(shí)該事務(wù)id整個(gè)數(shù)據(jù)庫(kù)共用。

此時(shí),mysql在一個(gè)叫做undo的回滾日志棧中存在著一條舊有的數(shù)據(jù),在更新操作執(zhí)行的時(shí)候,會(huì)押入當(dāng)前更新的數(shù)據(jù),同時(shí)記錄下當(dāng)前的事務(wù)id,并且產(chǎn)生一條指向前面押入的舊有數(shù)據(jù)的回滾指針。

即此時(shí),回滾日志棧的結(jié)構(gòu)如下


image-20200229213312983.png

同理,我們接下來(lái)運(yùn)行第3行sql

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1

由于事務(wù)3運(yùn)行了新的更新語(yǔ)句,一個(gè)新的事務(wù)id3生成,并且押入了undo中,即此時(shí)結(jié)構(gòu)如下

image-20200229213727288.png

同理,運(yùn)行第4,5行

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit

之后結(jié)構(gòu)如下

image-20200229213944090.png

運(yùn)行第6行

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1

我們可以看到,這一行的操作是查詢(xún)操作。對(duì)于該查詢(xún)操作,我們?nèi)绾伪WC該操作的正確性?

當(dāng)?shù)?行執(zhí)行查詢(xún)操作的時(shí)候,會(huì)生成一個(gè)叫做read-view的一致性視圖,該視圖的結(jié)構(gòu)如下

  • [min_id,min_id+1,...]-max_id

如上圖所示,該視圖的結(jié)構(gòu)由一個(gè)id數(shù)組和一個(gè)id組成;

其中,數(shù)組包含了所有未提交的事務(wù)的id,而其中min_id是未提交的最小事務(wù)id。max_id則是已經(jīng)創(chuàng)建的最大事務(wù)id,該id不關(guān)心是否已經(jīng)提交。

那么,結(jié)合我們的事務(wù)的執(zhí)行情況,則第六行生成的read-view為[2,3]-4。

接下來(lái)我們看圖

image-20200229220109912.png

根據(jù)min_id和max_id的區(qū)分,我們將每個(gè)事務(wù)id(trx_id)區(qū)分為;

  1. 已經(jīng)提交的事務(wù)id;(trx_id<min_id)
  2. 未提交或者可能提交了事務(wù)id;(min_id<=trx_id<=max_id)
  3. 當(dāng)前查詢(xún)語(yǔ)句之后才開(kāi)始的事務(wù)id(max_id<trx_id)

然后,通過(guò)使用生成的read-view,從undo棧的棧頂開(kāi)始逐行查詢(xún),之后通過(guò)以下規(guī)則鑒定可見(jiàn)性,如果當(dāng)前查詢(xún)行的數(shù)據(jù)可見(jiàn),將返回對(duì)應(yīng)的結(jié)果。

一、 查詢(xún)行的版本號(hào)如果小于min_id,即為已經(jīng)提交的事務(wù)(trx_id<min_id),那么這個(gè)數(shù)據(jù)是可見(jiàn)的。
二、 查詢(xún)行的版本號(hào)如果大于max_id,即為已經(jīng)提交的事務(wù)(max_id<trx_id),那么這個(gè)數(shù)據(jù)是不可見(jiàn)的。
三、 查詢(xún)行的版本號(hào)如果處于min_id<=trx_id<=max_id的區(qū)間內(nèi),包括兩種情況:
   1. 如果查詢(xún)行的trx_id不在數(shù)組中,說(shuō)明這個(gè)版本是已經(jīng)提交了的事務(wù),那么這個(gè)數(shù)據(jù)可見(jiàn)
   2. 如果查詢(xún)行的trx_id在數(shù)據(jù)中,說(shuō)明這個(gè)版本是還沒(méi)提交的事務(wù),那么:如果該版本不是當(dāng)前自己事務(wù)的版本號(hào),不可見(jiàn),如果該版本是當(dāng)前自己事務(wù)的版本號(hào),可見(jiàn)。

結(jié)合上面的規(guī)則,我們來(lái)看這條查詢(xún)語(yǔ)句:

  1. 語(yǔ)句執(zhí)行后,read-view=[2,3]-4
  2. 查詢(xún)棧頂,該行的版本號(hào)為4,即屬于第三種情況,并且該事務(wù)已經(jīng)提交,不在數(shù)組中,可見(jiàn),返回結(jié)果:name4

運(yùn)行第7,8行

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1

此時(shí)undo結(jié)構(gòu)如下

image-20200229222812963.png

運(yùn)行行數(shù)9

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
1 begin begin begin begin Begin
2 Update A set name='name2' where id=1
3 Update A set name='name3' where id=1
4 Update A set name='name4' where id=1
5 Commit
6 Select name from A where id = 1
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1
9 Select name from A where id = 1

第9行我們?cè)诓樵?xún)1中,運(yùn)行了新的查詢(xún)語(yǔ)句,我們來(lái)看這條查詢(xún)語(yǔ)句

image-20200229223451610.png
  1. 語(yǔ)句執(zhí)行后,read-view=[2,3]-4
  2. 從棧頂開(kāi)始查詢(xún),該行的版本號(hào)為2,則屬于第三種情況,同時(shí)存在于數(shù)組中,即該行數(shù)據(jù)不可見(jiàn)
  3. 繼續(xù)查詢(xún)下一行,結(jié)果同2
  4. 繼續(xù)查詢(xún)下一行,版本號(hào)為4,不存在于數(shù)組中,可見(jiàn),返回結(jié)果name4

運(yùn)行第10,11,12行

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1
9 Select name from A where id = 1
10 commit; Update A set name='name3-1' where id=1
11 Update A set name='name3-2' where id=1
12

這行我們提交了事務(wù)2,而且事務(wù)3進(jìn)行了兩次更新,此時(shí)數(shù)據(jù)格式如下:

image-20200229224639347.png

運(yùn)行第11行

0 事務(wù)2 事務(wù)3 事務(wù)4 查詢(xún)1 查詢(xún)2
7 Update A set name='name2-1' where id=1
8 Update A set name='name2-2' where id=1
9 Select name from A where id = 1
10 commit;
Select name from A where id = 1 Select name from A where id = 1

這一行我們將查詢(xún)1和查詢(xún)2放置在一起進(jìn)行比較。

首先我們看查詢(xún)2的語(yǔ)句:

image-20200229224719402.png
  1. 查詢(xún)語(yǔ)句生成read-view=[3]-4
  2. 從棧頂開(kāi)始查詢(xún),該行的版本號(hào)為3,則屬于第三種情況,同時(shí)存在于數(shù)組中,即該行數(shù)據(jù)不可見(jiàn)
  3. 繼續(xù)查詢(xún)下一行,結(jié)果同2
  4. 繼續(xù)查詢(xún)下一行,版本號(hào)為2,不存在于數(shù)組中,可見(jiàn),返回結(jié)果name2-1

接下來(lái),我們看查詢(xún)1的查詢(xún)情況:

image-20200229225016097.png

如上圖所示,mysql的事務(wù)分為可重復(fù)讀,與不可重復(fù)讀的情況。

如果是重復(fù)讀的情況,每次查詢(xún)將使用第一次生成的read-view;而如果是不可重復(fù)讀的情況,那么每次查詢(xún)都將使用新的read-view。即如果是可重復(fù),處理情況如下

  1. 查詢(xún)語(yǔ)句生成read-view=[2,3]-4
  2. 從棧頂開(kāi)始查詢(xún),該行的版本號(hào)為3,則屬于第三種情況,同時(shí)存在于數(shù)組中,即該行數(shù)據(jù)不可見(jiàn)
  3. 繼續(xù)查詢(xún)下一行,結(jié)果同3
  4. 繼續(xù)查詢(xún)下一行,版本號(hào)為2,則屬于第三種情況,同時(shí)存在于數(shù)組中,即該行數(shù)據(jù)不可見(jiàn)
  5. 繼續(xù)查詢(xún)下一行,結(jié)果同4
  6. 繼續(xù)查詢(xún)下一行,版本號(hào)為4,即屬于第三種情況,并且不存在于數(shù)組中,即該行數(shù)據(jù)可見(jiàn),返回?cái)?shù)據(jù)為name4。

由上面的情況即可以發(fā)現(xiàn),兩個(gè)查詢(xún)事件就算同一時(shí)間查詢(xún),結(jié)果也是可能不同的。

對(duì)于查詢(xún)的版本號(hào)小于min_id和大于min_id的情況,相對(duì)簡(jiǎn)單這里就不做討論了。

而刪除操作,在mysql中視作一種特殊的更新操作,會(huì)復(fù)制原來(lái)的數(shù)據(jù),并更新?tīng)顟B(tài)為已刪除,然后壓入undo棧中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們都知道事務(wù)的幾種性質(zhì),數(shù)據(jù)庫(kù)為了維護(hù)這些性質(zhì),尤其是一致性和隔離性,一般使用加鎖這種方式。同時(shí)數(shù)據(jù)庫(kù)又是個(gè)高并...
    流浪冰007閱讀 1,129評(píng)論 0 2
  • .數(shù)據(jù)庫(kù) 數(shù)據(jù)庫(kù)的發(fā)展: 文件系統(tǒng)(使用磁盤(pán)文件來(lái)存儲(chǔ)數(shù)據(jù))=>第一代數(shù)據(jù)庫(kù)(出現(xiàn)了網(wǎng)狀模型,層次模型的數(shù)據(jù)庫(kù))=...
    小Q逛逛閱讀 1,069評(píng)論 0 2
  • 最近有個(gè)業(yè)務(wù)需要能夠存儲(chǔ)Json并做一些簡(jiǎn)單的業(yè)務(wù)邏輯處理。業(yè)務(wù)找到我說(shuō)json的數(shù)據(jù)分析很難用mysql 5.6...
    Jannonx閱讀 2,920評(píng)論 0 1
  • 什么是數(shù)據(jù)庫(kù)? 數(shù)據(jù)庫(kù)是存儲(chǔ)數(shù)據(jù)的集合的單獨(dú)的應(yīng)用程序。每個(gè)數(shù)據(jù)庫(kù)具有一個(gè)或多個(gè)不同的API,用于創(chuàng)建,訪問(wèn),管理...
    chen_000閱讀 4,124評(píng)論 0 19
  • ORACLE自學(xué)教程 --create tabletestone ( id number, --序號(hào)usernam...
    落葉寂聊閱讀 1,232評(píng)論 0 0

友情鏈接更多精彩內(nèi)容