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

MySQL MVVC多版本并發(fā)控制的實(shí)現(xiàn)詳解

 更新時(shí)間:2022年08月24日 11:21:54   作者:清風(fēng)拂來水波不興  
在多版本并發(fā)控制中,為了保證數(shù)據(jù)操作在多線程過程中,保證事務(wù)隔離的機(jī)制,降低鎖競(jìng)爭(zhēng)的壓力,保證較高的并發(fā)量。在每開啟一個(gè)事務(wù)時(shí),會(huì)生成一個(gè)事務(wù)的版本號(hào),被操作的數(shù)據(jù)會(huì)生成一條新的數(shù)據(jù)行

一、概述

MVCC(Multiversion Concurrency Control),多版本并發(fā)控制。它和undo log中的版本鏈息息相關(guān),MVVC通過數(shù)據(jù)行的多個(gè)版本來實(shí)現(xiàn)數(shù)據(jù)庫的并發(fā)控制。

簡(jiǎn)單的說就是當(dāng)前事務(wù)查詢另一個(gè)事務(wù)正在更改的行(如果此時(shí)讀取就會(huì)發(fā)生臟讀),不用加鎖等待,而是讀取該數(shù)據(jù)的歷史版本,降低響應(yīng)時(shí)間。

MVVC是通過undo log和Read View兩種技術(shù)實(shí)現(xiàn)的。

二、快照讀與當(dāng)前讀

MVCC在MySQL InnoDB中的實(shí)現(xiàn)主要是為了提高數(shù)據(jù)庫并發(fā)性能,用更好的方式去處理讀-寫沖突,做到即使有讀寫沖突時(shí),也能做到不加鎖,非阻塞并發(fā)讀 ,而這個(gè)讀指的就是快照讀 , 而非當(dāng)前讀。當(dāng)前讀實(shí)際上是一種加鎖的操作。

1.當(dāng)前讀

當(dāng)前讀讀取的記錄一定是最新的數(shù)據(jù),讀取時(shí)還要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄,會(huì)對(duì)讀取的記錄進(jìn)行加鎖。

加鎖的讀被稱為當(dāng)前讀,還有數(shù)據(jù)的增刪改都是要先讀取數(shù)據(jù)的,這一讀取過程也是當(dāng)前讀。

SELECT * FROM t LOCK IN SHARE MODE; # 共享鎖
SELECT * FROM t FOR UPDATE; # 排他鎖
UPDATE SET t..

2.快照讀

快照讀又叫一致性讀,讀取的是數(shù)據(jù)行的快照版本。在MySQL中,普通的select語句(不加for update或lock in share mode的select語句)默認(rèn)就是使用的快照讀,不加鎖。

SELECT * FROM table WHERE ...

之所以這樣,是因?yàn)榭煺兆x可以避免加鎖操作,降低開銷。

當(dāng)事務(wù)的隔離級(jí)別是串行時(shí),快照讀就沒有用了,會(huì)退化為當(dāng)前讀。

三、隔離級(jí)別與版本鏈復(fù)習(xí)

隔離級(jí)別:

在MySQL中默認(rèn)的隔離級(jí)別就是可重復(fù)讀RR,可以解決不可重復(fù)讀問題,在MySQL中,特別的還額外支持解決幻讀問題。

它是如何解決幻讀問題的呢?有兩種方式:

  • 使用間隙鎖和臨鍵鎖解決,簡(jiǎn)而言之就是加鎖,在此期間其他事務(wù)不能夠插入數(shù)據(jù)
  • MVCC方式,無需加鎖,消耗低(缺點(diǎn)是沒有完全解決幻讀問題)。

undo log版本鏈:

對(duì)應(yīng)InnoDB來說,聚簇索引中的每個(gè)記錄都包含了兩個(gè)必要的隱藏字段:

  • trx_id:每次一個(gè)事務(wù)對(duì)某條聚簇索引記錄進(jìn)行改動(dòng)時(shí),都會(huì)把該事務(wù)的事務(wù)id賦值給trx_id隱藏列。
  • roll_pointer:回滾指針,每次修改數(shù)據(jù)時(shí),都會(huì)把舊數(shù)據(jù)放入undo log日志中,新的數(shù)據(jù)指向該舊數(shù)據(jù),做成一個(gè)版本鏈,該指針字段就稱為回滾指針,通過該指針可以找到修改前的數(shù)據(jù)。

舉例:

有一個(gè)id為8的事務(wù)創(chuàng)建了一條數(shù)據(jù),那么該記錄的示意圖大概如下:

假設(shè)之后兩個(gè)id分別為10、20的事務(wù)對(duì)這條記錄進(jìn)行update操作,流程如下:

事務(wù)10事務(wù)20
BEGIN; 
 BEGIN;
UPDATE student SET name='李四' WHERE id=1; 
UPDATE student SET name='王五' WHERE id=1; 
COMMIT; 
 UPDATE student SET name='趙六' WHERE id=1;
 UPDATE student SET name='錢七' WHERE id=1;
 COMMIT;

每次修改都會(huì)生成一個(gè)undo log日志,每個(gè)日志都相互鏈接,構(gòu)成版本鏈,此時(shí)該條數(shù)據(jù)的示意圖如下:

每個(gè)版本中還包含生成該版本時(shí)對(duì)應(yīng)的事務(wù)id 。

四、Read View

有了undo log就可以讀取到記錄的歷史版本,那么在什么情況下,讀取哪個(gè)版本的記錄呢?這就用到了Read View,它幫我們解決了行的可見性問題。

Read View就是當(dāng)某個(gè)事務(wù)在使用MVVC機(jī)制進(jìn)行快照讀操作時(shí)產(chǎn)生的讀視圖。該視圖是數(shù)據(jù)庫當(dāng)前所有活躍事務(wù)id(還未提交的事務(wù))組成的列表的一個(gè)快照。

1.實(shí)現(xiàn)原理

四種隔離級(jí)別里,讀未提交和串行化是不會(huì)使用MVVC的,因?yàn)樽x未提交直接讀取某個(gè)數(shù)據(jù)的最新數(shù)據(jù)即可,串行化是通過加鎖來讀的。

讀已提交和可重復(fù)讀都必須保證讀到的數(shù)據(jù)都是其他事務(wù)提交了的,所以,其他事務(wù)修改了數(shù)據(jù)但是還未提交,我們不能夠訪問該數(shù)據(jù),但可以通過MVVC機(jī)制讀取該記錄的歷史版本,核心問題就是需要判斷版本鏈中的哪條歷史版本是當(dāng)前事務(wù)可見的,這也是ReadView要解決的問題。

Read View包含4個(gè)比較重要的內(nèi)容:

  • creator_trx_id:創(chuàng)建這個(gè)Read View的事務(wù)id,Read View和事務(wù)是一一對(duì)應(yīng)的。

只有事務(wù)對(duì)表中的記錄做修改時(shí)才會(huì)為事務(wù)分配事務(wù)id,否則一個(gè)事務(wù)中只有讀操作,該事務(wù)的id默認(rèn)為0。

  • trx_ids:表示在生成Read View時(shí)當(dāng)前系統(tǒng)中活躍的事務(wù)id列表。提交了的事務(wù)不在其中。
  • up_limit_id:活躍的事務(wù)中最小的事務(wù)id。
  • low_limit_id:表示生成Read View時(shí)系統(tǒng)應(yīng)該分配給下一個(gè)事務(wù)的id值,同樣也表示系統(tǒng)中最大的事務(wù)id值。

注意:low_limit_id并不是trx_ids中的最大值,事務(wù)id是遞增分配的。比如,現(xiàn)在有id為1, 2,5這三個(gè)事務(wù),之后id為5的事務(wù)提交了。那么一個(gè)新的讀事務(wù)在生成ReadView時(shí), trx_ids就包括1和2,up_limit_id的值就是1,low_limit_id的值就是6。

2.Read View規(guī)則

版本鏈

當(dāng)某個(gè)事務(wù)有了Read View,訪問某條記錄時(shí),需要按照下面的步驟判斷該記錄的哪個(gè)版本可見:

  • 如果該版本記錄的trx_id和Read View的creator_trx_id相同,意味著該版本的記錄是由當(dāng)前事務(wù)修改的,因此該版本可以被當(dāng)前事務(wù)訪問
  • 如果該版本記錄的trx_id小于Read View的up_limit_id,證明當(dāng)前事務(wù)生成Read View時(shí),此事務(wù)已經(jīng)提交了,所以當(dāng)前事務(wù)可以讀取該版本。
  • 如果該版本的trx_id大于等于low_limit_id,證明生成該版本的事務(wù)在當(dāng)前事務(wù)生成Read View之后才開啟,所以該版本不可以被當(dāng)前事務(wù)訪問。
  • 如果被訪問版本的trx_id屬性值在ReadView的up_limit_id和low_limit_id之間,那就需要判斷一下trx_id屬性值是不是在trx_ids列表中,如果不在的話才能訪問,否則不能訪問。

3.整體流程

了解了這些概念之后,我們來看下當(dāng)查詢一條記錄的時(shí)候,系統(tǒng)如何通過MVCC找到它:

  • 首先獲取事務(wù)自己的版本號(hào),也就是事務(wù)ID;
  • 獲取 ReadView;
  • 查詢得到的數(shù)據(jù),然后與 ReadView 中的事務(wù)版本號(hào)進(jìn)行比較;
  • 如果不符合 ReadView 規(guī)則,就需要從Undo Log中獲取歷史快照;
  • 最后返回符合規(guī)則的數(shù)據(jù)。

在隔離級(jí)別為讀已提交時(shí),一個(gè)事務(wù)中的每一次SELECT查詢都會(huì)重新獲取一次Read View,而可重復(fù)讀是第一SELECT操作才會(huì)生成Read View,之后的查詢操作復(fù)用這一個(gè)。

導(dǎo)致這兩種的差距是因?yàn)椋嚎芍貜?fù)讀要保證一個(gè)事務(wù)中相同的SELECT讀取的內(nèi)容是相同的。

五、舉例

1.READ

COMMITTED隔離級(jí)別下

現(xiàn)在有兩個(gè)事務(wù)id分別為10、20的事務(wù)在執(zhí)行:

-- id為10的事務(wù)
begin;
update t set name='李四' where id=1;
update t set name='王五' where id=1;
-- id為20的事務(wù)
更新其他行的數(shù)據(jù)

此刻,表中id為1的記錄得到的版本鏈表如下所示:

此時(shí)新來一個(gè)事務(wù)執(zhí)行如下操作:

begin;
select * from t where id=1;
-- 事務(wù)10、20未提交

查詢到的結(jié)果為張三。

具體的過程如下:

  • 在執(zhí)行select語句前,先生成一個(gè)Read View,Read View的creator_trx_id為0,trx_ids列表的內(nèi)容是[10,20],up_limit_id為10,low_limit_id為21。
  • 查詢name為王五的最新版本的記錄,按規(guī)則進(jìn)行對(duì)比,因?yàn)閠rx_id為10,10剛好是trx_ids中的記錄,所以這條記錄對(duì)當(dāng)前事務(wù)不可見,根據(jù)回滾指針得到下一個(gè)版本
  • 下一個(gè)版本name為李四,也不行
  • 繼續(xù)找到name為張三的版本,trx_id為8,8小于up_limit_id,所以該版本對(duì)當(dāng)前事務(wù)可見,得到最終結(jié)果

接下來,再將id為10的事務(wù)進(jìn)行commit提交。然后id為20的事務(wù)來更新記錄:

begin;
-- id為20的事務(wù)
update t set name='趙六' where id=1;
update t set name='錢七' where id=1;

此時(shí)版本鏈更新為:

再到剛才使用READ COMMITTED隔離級(jí)別的事務(wù)中繼續(xù)查找這個(gè)id 為1的記錄,得到的結(jié)果為name=王五的那條記錄。執(zhí)行過程如下:

  • 生成Read View,Read View的creator_trx_id為0,trx_ids列表的內(nèi)容是[20],up_limit_id為20,low_limit_id為21。
  • 因?yàn)榍皟蓚€(gè)版本的記錄trx_id為20,存在trx_ids中,所以跳過
  • 到第三條記錄時(shí),trx_id為10,小于20,可以讀取,所以最終結(jié)果為王五

注意:READ COMMITTED,每次讀取數(shù)據(jù)前都生成一個(gè)新的ReadView。

2.REPEATABLE READ隔離級(jí)別下

假如此時(shí)id為10的事務(wù)和id為20的事務(wù)正在修改,都未提交,修改內(nèi)容和前面的一樣,但是還未提交,此時(shí)當(dāng)前事務(wù)做一個(gè)查詢。

步驟為:

  • 生成Read View,Read View的creator_trx_id為0,trx_ids列表的內(nèi)容是[10,20],up_limit_id為10,low_limit_id為21。
  • trx_id為10和20的都不滿足要求
  • 最后查找到name為張三的歷史版本的數(shù)據(jù)

此時(shí),id為10的記錄提交事務(wù)。

當(dāng)前事務(wù)又需要select id為1的記錄,步驟為:

  • 因?yàn)槭强芍貜?fù)讀,且第一次select已經(jīng)生成過Read View了,所有會(huì)復(fù)用它,不重新生成。
  • 所以trx_id為10和20的記錄依舊不符合規(guī)則,最終得到的數(shù)據(jù)還是張三,符合可重復(fù)讀的規(guī)范

注意:REPEATABLE READ,每次讀取都復(fù)用第一次生成的Read View

3.如何解決幻讀

假設(shè)現(xiàn)在有一條數(shù)據(jù),id為1

當(dāng)前活躍的事務(wù)有10和20。

此時(shí)當(dāng)前事務(wù)啟動(dòng)了,執(zhí)行如下SQL語句:

begin;
select * from student where id>=1;

在開始前生成Read View,內(nèi)容如下:creator_trx_id=0,trx_ids= [10,20] , up_limit_id=10, low_limit_id=21。

由于id大于等于1的數(shù)據(jù)只有一個(gè),且該數(shù)據(jù)的trx_id為8,小于up_limit_id,所以可以讀取到。

在這之后id為10的事務(wù)新增了一行數(shù)據(jù),增加了id為2的數(shù)據(jù),且提交了。

此時(shí)當(dāng)前線程繼續(xù)查找id>=1的數(shù)據(jù),因?yàn)槭强芍貜?fù)讀,復(fù)用剛剛的Read View。

得到兩行數(shù)據(jù),但是因?yàn)閕d為2的數(shù)據(jù)trx_id為10,該值在Read View的trx_ids中存在,所以該記錄對(duì)當(dāng)前事務(wù)不可見,所以最后查詢到的數(shù)據(jù)只有一條記錄。

如果當(dāng)前事務(wù)再插入id為2的數(shù)據(jù)就插不進(jìn)去,所以說MVVC只解決了一半的幻讀問題。

到此這篇關(guān)于MySQL MVVC多版本并發(fā)控制的實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)MySQL MVVC內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MYSQL事務(wù)回滾的2個(gè)問題分析

    MYSQL事務(wù)回滾的2個(gè)問題分析

    在事務(wù)中,每個(gè)正確的原子操作都會(huì)被順序執(zhí)行,直到遇到錯(cuò)誤的原子操作,此時(shí)事務(wù)會(huì)將之前的操作進(jìn)行回滾?;貪L的意思是如果之前是插入操作,那么會(huì)執(zhí)行刪 除插入的記錄,如果之前是update操作,也會(huì)執(zhí)行update操作將之前的記錄還原
    2014-05-05
  • MySQL性能分析及explain的使用說明

    MySQL性能分析及explain的使用說明

    本文我們主要介紹了MySQL性能分析以及explain的使用,包括:組合索引、慢查詢分析、MYISAM和INNODB的鎖定、MYSQL的事務(wù)配置項(xiàng)等,希望能夠?qū)δ兴鶐椭?/div> 2011-08-08
  • MySQL的mysqldump工具用法詳解

    MySQL的mysqldump工具用法詳解

    這篇文章主要介紹了MySQL的mysqldump工具用法詳解,同時(shí)附帶了相關(guān)Source命令的用法,詳解需要的朋友可以參考下
    2015-07-07
  • MYSQL實(shí)現(xiàn)添加購物車時(shí)防止重復(fù)添加示例代碼

    MYSQL實(shí)現(xiàn)添加購物車時(shí)防止重復(fù)添加示例代碼

    在向mysql中插入數(shù)據(jù)的時(shí)候最需要注意的就是防止重復(fù)發(fā)添加數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于MYSQL如何實(shí)現(xiàn)添加購物車的時(shí)候防止重復(fù)添加的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-09-09
  • MySQL如何開啟用戶遠(yuǎn)程登錄權(quán)限

    MySQL如何開啟用戶遠(yuǎn)程登錄權(quán)限

    這篇文章主要介紹了MySQL如何開啟用戶遠(yuǎn)程登錄權(quán)限問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • MySQL內(nèi)存表的特性與使用介紹

    MySQL內(nèi)存表的特性與使用介紹

    臨時(shí)表和內(nèi)存表都可以人工創(chuàng)建,但臨時(shí)表更多的作用是系統(tǒng)自己創(chuàng)建后,組織數(shù)據(jù)以提升性能,如子查詢,臨時(shí)表在多個(gè)連接之間不能共享。這里只討論內(nèi)存表
    2013-02-02
  • Mysql如何優(yōu)化查詢速度

    Mysql如何優(yōu)化查詢速度

    這篇文章主要介紹了Mysql如何優(yōu)化查詢速度問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • mysql 8.0.20 winx64安裝配置方法圖文教程

    mysql 8.0.20 winx64安裝配置方法圖文教程

    這篇文章主要為大家詳細(xì)介紹了mysql 8.0.20 winx64安裝配置方法圖文教程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • MySQL出現(xiàn)Waiting for table metadata lock的原因方法

    MySQL出現(xiàn)Waiting for table metadata lock的原因方法

    在本篇內(nèi)容里小編給大家整理了MySQL出現(xiàn)Waiting for table metadata lock的原因以及解決方法對(duì)此有需要的朋友們學(xué)習(xí)下。
    2019-05-05
  • MySQL數(shù)據(jù)庫表的合并及分區(qū)方式

    MySQL數(shù)據(jù)庫表的合并及分區(qū)方式

    這篇文章主要介紹了MySQL數(shù)據(jù)庫表的合并及分區(qū)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08

最新評(píng)論