日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

图文结合带你搞懂InnoDB MVCC

發(fā)布時(shí)間:2024/3/13 c/c++ 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图文结合带你搞懂InnoDB MVCC 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
  • GreatSQL社區(qū)原創(chuàng)內(nèi)容未經(jīng)授權(quán)不得隨意使用,轉(zhuǎn)載請(qǐng)聯(lián)系小編并注明來(lái)源。
  • GreatSQL是MySQL的國(guó)產(chǎn)分支版本,使用上與MySQL一致。

    • 前情提要
    • 當(dāng)前讀
    • 快照讀
  • 什么是MVCC
    • 三個(gè)隱藏字段
    • Undo Log回滾日志
    • MVCC版本鏈
    • ReadView讀視圖
  • 不同隔離級(jí)別下MVCC分析
    • READ-COMMITTED隔離級(jí)別
    • REPEATABLE-READ隔離級(jí)別

前情提要

事務(wù)有四大特性ACID分別是:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)

其中隔離性是通過(guò)數(shù)據(jù)庫(kù)的鎖加上MVCC(多版本并發(fā)控制)來(lái)保證的。

在介紹MVCC之前先來(lái)了解一下當(dāng)前讀和快照讀。

當(dāng)前讀

當(dāng)前讀讀取的是記錄的最新版本。同時(shí)在讀取的時(shí)候還要保證其他的并發(fā)事務(wù)不能更改當(dāng)前記錄,那么當(dāng)前讀會(huì)對(duì)它要讀取的記錄進(jìn)行加鎖。不同的操作會(huì)加上不同類型的鎖,如:SELECT ... LOCK IN SHARE MODE(共享鎖),SELECT ... FOR UPDATE、UPDATE、INSERT、 DELETE(排他鎖)。

快照讀

簡(jiǎn)單的不加鎖的SELECT就是快照讀,快照讀讀取的是快照生成時(shí)的數(shù)據(jù),不一定是最新的數(shù)據(jù),它是不加鎖的非阻塞讀。而不同隔離級(jí)別下,創(chuàng)建快照的時(shí)機(jī)也不同:

  • READ-COMMITTED(讀已提交):事務(wù)每次SELECT時(shí)創(chuàng)建ReadView
  • REPEATABLE-READ(可重復(fù)讀):事務(wù)第一次SELECT時(shí)創(chuàng)建ReadView,后續(xù)一直使用

在MySQL默認(rèn)隔離級(jí)別(REPEATABLE-READ)下,快照讀保證了數(shù)據(jù)的可重復(fù)讀。

什么是MVCC

MVCC全稱Multi-Version Concurrency Control,即多版本并發(fā)控制。它是一種并發(fā)控制的方法,它可以維護(hù)一個(gè)數(shù)據(jù)的多個(gè)版本,用更好的方式去處理讀寫沖突,做到即使有讀寫沖突也能不加鎖。MySQL中MVCC的具體實(shí)現(xiàn),還需要依賴于表中的三個(gè)隱藏字段、Undo Log日志以及ReadView。

三個(gè)隱藏字段

mysql> SHOW CREATE TABLE stu \G; *************************** 1. row ***************************Table: stu Create Table: CREATE TABLE `stu` (`id` int NOT NULL,`name` varchar(10) NOT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)mysql> SELECT * FROM stu; +----+--------+ | id | name | +----+--------+ | 1 | m | | 2 | f | +----+--------+

當(dāng)創(chuàng)建了上述這張表后,我們?cè)诓榭幢斫Y(jié)構(gòu)時(shí)只能看到id、name字段,實(shí)際上除了這兩個(gè)字段外,InnoDB引擎還自動(dòng)為我們添加了三個(gè)隱藏字段,見(jiàn)下表:

字段含義
DB_TRX_ID最近修改事務(wù)ID,記錄插入這條記錄或最后一次修改該記錄的事務(wù)ID。
DB_ROLL_PTR回滾指針,指向這條記錄的上一個(gè)版本,用于配合Undo Log,指向上一個(gè)版本。
DB_ROW_ID隱藏主鍵,如果表結(jié)構(gòu)沒(méi)有指定主鍵,將會(huì)生成該隱藏字段。

我們可以使用ibd2sdi工具來(lái)從表空間文件中提取序列化的字典信息(SDI),來(lái)驗(yàn)證一下這三個(gè)隱藏字段是否存在。

["ibd2sdi" , {"type": 1,"id": 402,"object":{"mysqld_version_id": 80025,"dd_version": 80023,"sdi_version": 80019,"dd_object_type": "Table","dd_object": {"name": "stu","mysql_version_id": 80025,"created": 20220919023413,"last_altered": 20220919023413,"hidden": 1,"options": "avg_row_length=0;encrypt_type=N;explicit_encryption=0;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0;","columns": [{"name": "id","type": 4,"is_nullable": false,"is_zerofill": false,"is_unsigned": false,"is_auto_increment": false,"is_virtual": false,"hidden": 1, ···省略},{"name": "name","type": 16,"is_nullable": false,"is_zerofill": false,"is_unsigned": false,"is_auto_increment": false,"is_virtual": false,"hidden": 1, ···省略},{"name": "DB_TRX_ID", #最近修改事務(wù)ID"type": 10,"is_nullable": false,"is_zerofill": false,"is_unsigned": false,"is_auto_increment": false,"is_virtual": false,"hidden": 2, ···省略},{"name": "DB_ROLL_PTR", #回滾指針"type": 9,"is_nullable": false,"is_zerofill": false,"is_unsigned": false,"is_auto_increment": false,"is_virtual": false,"hidden": 2, ···省略}],

注意:因?yàn)檫@張表里已經(jīng)指定了主鍵為id列,所以不會(huì)生成隱藏主鍵DB_ROW_ID列。

Undo Log回滾日志

回滾日志,在增、改、刪操作的時(shí)候產(chǎn)生的便于數(shù)據(jù)回滾的日志。當(dāng)INSERT操作的時(shí)候,產(chǎn)生的回滾日志在事務(wù)提交后可被立即刪除。而UPDATE和DELETE操作的時(shí)候,產(chǎn)生的Undo Log日志不僅在進(jìn)行數(shù)據(jù)回滾時(shí)需要,在進(jìn)行快照讀時(shí)也需要,所以不會(huì)立即被刪除。

Undo Log詳情可見(jiàn)文章:待浩源Undo Log文章發(fā)表后添加

MVCC版本鏈

當(dāng)有多個(gè)并發(fā)事務(wù)操作一行數(shù)據(jù)時(shí),對(duì)這行數(shù)據(jù)的修改會(huì)產(chǎn)生多個(gè)版本,多個(gè)版本通過(guò)上述的一個(gè)隱藏字段DB_ROLL_PTR回滾指針指向Undo Log數(shù)據(jù)地址形成一個(gè)鏈表,即MVCC版本鏈。

ReadView讀視圖

ReadView讀視圖是快照讀SQL執(zhí)行時(shí)MVCC提取數(shù)據(jù)的依據(jù),記錄并維護(hù)系統(tǒng)當(dāng)前活躍的事務(wù)(未提交的)id。

上面講過(guò)Undo Log和MVCC版本鏈,一條數(shù)據(jù)經(jīng)過(guò)多次修改會(huì)產(chǎn)生多個(gè)版本,而快照讀是根據(jù)不同時(shí)機(jī)創(chuàng)建的快照獲取數(shù)據(jù)的,那么快照讀SQL在執(zhí)行時(shí)該讀取那個(gè)版本的數(shù)據(jù)就是靠ReadViw讀視圖來(lái)決定的。

ReadView讀視圖中包含了四個(gè)核心字段,也是讀取數(shù)據(jù)的判斷依據(jù): | 字段 | 含義 | | -------------- | ---------------------------------------------------- | | m_ids | 當(dāng)前活躍的事務(wù)ID集合 | | min_trx_id | 最小活躍事務(wù)ID | | max_trx_id | 預(yù)分配事務(wù)ID,當(dāng)前最大事務(wù)ID+1(因?yàn)槭聞?wù)ID是自增的) | | creator_trx_id | ReadView創(chuàng)建者的事務(wù)ID |

ReadView一共有四種匹配規(guī)則: | 條件 | 能否訪問(wèn) | 說(shuō)明 | | ---------------------------------- | ----------------------------------------- | -------------------------------------------- | | trx_id == creatro_trx_id | 可以訪問(wèn)該版本 | 成立,說(shuō)明數(shù)據(jù)是當(dāng)前這個(gè)事務(wù)更改的。 | | trx_id < min_trx_id | 可以訪問(wèn)該版本 | 成立,說(shuō)明數(shù)據(jù)已經(jīng)提交了。 | | trx_id > max_trx_id | 不可以訪問(wèn)該版本 | 成立,說(shuō)明該事務(wù)是在ReadView生成后才開(kāi)啟的。 | | min_trx_id <= trx_id <= max_trx_id | 如果trx_id不在m_ids中,那么可以訪問(wèn)該版本 | 成立,說(shuō)明數(shù)據(jù)已經(jīng)提交。 |

不同隔離級(jí)別下MVCC分析

READ-COMMITTED隔離級(jí)別

前面有提到過(guò)在READ-COMMITTED隔離級(jí)別下事務(wù)在每次快照讀SQL執(zhí)行時(shí)創(chuàng)建ReadView,每次創(chuàng)建的ReadView的四個(gè)字段對(duì)應(yīng)的值也是不同的,所以在READ-COMMITTED隔離級(jí)別下每次快照讀SQL獲取的數(shù)據(jù)可能也是不同的。

下面通過(guò)一個(gè)READ-COMMITTED隔離級(jí)別下并發(fā)事務(wù)的案例來(lái)詳細(xì)看看:

現(xiàn)有四個(gè)并發(fā)事務(wù)同時(shí)訪問(wèn)一條數(shù)據(jù):

在上述并發(fā)事務(wù)中,事務(wù)5查詢了兩次id為1的數(shù)據(jù),因?yàn)楫?dāng)前的隔離級(jí)別設(shè)置為了READ-COMMITTED,事務(wù)在每次快照讀SQL執(zhí)行時(shí)創(chuàng)建一個(gè)ReadView,每次生成的ReadView中的四個(gè)字段值都不同。那么三次快照讀都會(huì)根據(jù)生成的ReadView中的字段進(jìn)行規(guī)則匹配,從而決定返回的數(shù)據(jù)。接下來(lái)看看流程:

事務(wù)5第一次快照讀解讀

事務(wù)5第一次進(jìn)行查詢時(shí)生成的ReadView以及原數(shù)據(jù)如下圖:

在匹配版本數(shù)據(jù)前,先與表中數(shù)據(jù)進(jìn)行匹配:

該數(shù)據(jù)對(duì)應(yīng)的DB_TRX_ID為3,此時(shí)MVCC就會(huì)通過(guò)ReadView帶著這條數(shù)據(jù)去進(jìn)行規(guī)則匹配:

首先是第一條規(guī)則db_trx_id == creator_trx_id,db_trx_id(3)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id < min_trx_id,db_trx_id(3)不小于min_trx_id(3)故不成立;

第三條規(guī)則db_trx_id > max_trx_id,db_trx_id(3)小于max_trx_id(6)故不成立;

第四條規(guī)則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(3)在min_trx(3)與max_trx_id(6)之間,但是同時(shí)處于m_ids(3,4,5)集合之中故也不成立。

經(jīng)過(guò)這次匹配,表中最新的數(shù)據(jù)無(wú)法匹配,故要與MVCC版本鏈中最上面的數(shù)據(jù)進(jìn)行規(guī)則匹配

與MVCC版本鏈中最上方的版本進(jìn)行匹配:

第一條規(guī)則db_trx_id(2)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id(2)小于min_trx_id(3),該版本的數(shù)據(jù)滿足匹配規(guī)則中的第二條,說(shuō)明數(shù)據(jù)已經(jīng)提交,此時(shí)匹配將終止并返回這個(gè)版本對(duì)應(yīng)的數(shù)據(jù)。

事務(wù)5第二次快照讀

因?yàn)楫?dāng)前事務(wù)的隔離級(jí)別為READ-COMMITTED(讀已提交),所以在每次快照讀的時(shí)候都會(huì)創(chuàng)建一個(gè)ReadView,所以事務(wù)5第二次進(jìn)行查詢時(shí)生成的ReadView以及原數(shù)據(jù)如下圖:

在匹配版本數(shù)據(jù)前,先與表中數(shù)據(jù)進(jìn)行匹配:

該數(shù)據(jù)對(duì)應(yīng)的DB_TRX_ID為4,此時(shí)MVCC就會(huì)通過(guò)ReadView帶著這條數(shù)據(jù)去進(jìn)行規(guī)則匹配:

首先是第一條規(guī)則db_trx_id == creator_trx_id,db_trx_id(4)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id < min_trx_id,db_trx_id(4)不小于min_trx_id(4)故不成立;

第三條規(guī)則db_trx_id > max_trx_id,db_trx_id(4)小于max_trx_id(6)故不成立;

第四條規(guī)則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(4)在min_trx(4)與max_trx_id(6)之間,但是同時(shí)處于m_ids(4,5)集合之中故也不成立。

經(jīng)過(guò)這次匹配,表中最新的數(shù)據(jù)無(wú)法匹配,故要與MVCC版本鏈中最上面的數(shù)據(jù)進(jìn)行規(guī)則匹配

與MVCC版本鏈中最上方的版本進(jìn)行匹配:

第一條規(guī)則db_trx_id(3)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id(3)小于min_trx_id(4),該版本的數(shù)據(jù)滿足匹配規(guī)則中的第二條,說(shuō)明數(shù)據(jù)已經(jīng)提交,此時(shí)匹配將終止并返回這個(gè)版本對(duì)應(yīng)的數(shù)據(jù)。

REPEATABLE-READ級(jí)別

現(xiàn)在來(lái)看看REPEATABLE-READ可重復(fù)讀隔離級(jí)別有什么不同的地方。同樣,有四個(gè)并發(fā)事務(wù)同時(shí)訪問(wèn)一條數(shù)據(jù):

在上述并發(fā)事務(wù)中,事務(wù)5查詢了兩次id為1的數(shù)據(jù),因?yàn)楫?dāng)前的隔離級(jí)別設(shè)置為了REPEATABLE-READ,事務(wù)在第一次快照讀SQL執(zhí)行時(shí)創(chuàng)建ReadView,后續(xù)該事務(wù)所有的快照讀都復(fù)用該ReadView。接下來(lái)看看流程:

事務(wù)5第一次快照讀解讀

事務(wù)5第一次進(jìn)行查詢時(shí)生成的ReadView以及原數(shù)據(jù)如下圖:

在匹配版本數(shù)據(jù)前,先與表中數(shù)據(jù)進(jìn)行匹配:

該數(shù)據(jù)對(duì)應(yīng)的DB_TRX_ID為3,此時(shí)MVCC就會(huì)通過(guò)ReadView帶著這條數(shù)據(jù)去進(jìn)行規(guī)則匹配:

首先是第一條規(guī)則db_trx_id == creator_trx_id,db_trx_id(3)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id < min_trx_id,db_trx_id(3)不小于min_trx_id(3)故不成立;

第三條規(guī)則db_trx_id > max_trx_id,db_trx_id(3)小于max_trx_id(6)故不成立;

第四條規(guī)則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(3)在min_trx(3)與max_trx_id(6)之間,但是同時(shí)處于m_ids(3,4,5)集合之中故也不成立。

經(jīng)過(guò)這次匹配,表中最新的數(shù)據(jù)無(wú)法匹配,故要與MVCC版本鏈中最上面的數(shù)據(jù)進(jìn)行規(guī)則匹配

與MVCC版本鏈中最上方的版本進(jìn)行匹配:

第一條規(guī)則db_trx_id(2)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id(2)小于min_trx_id(3),該版本的數(shù)據(jù)滿足匹配規(guī)則中的第二條,說(shuō)明數(shù)據(jù)已經(jīng)提交,此時(shí)匹配將終止并返回這個(gè)版本對(duì)應(yīng)的數(shù)據(jù)。

事務(wù)5第二次快照讀解讀

因?yàn)楫?dāng)前事務(wù)的隔離級(jí)別為REPEATABLE-READ(可重復(fù)讀),所以第二次快照讀也會(huì)沿用第一次快照讀時(shí)創(chuàng)建的ReadView,如下:

在匹配版本數(shù)據(jù)前,先與表中數(shù)據(jù)進(jìn)行匹配:

該數(shù)據(jù)對(duì)應(yīng)的DB_TRX_ID為4,此時(shí)MVCC就會(huì)通過(guò)ReadView帶著這條數(shù)據(jù)去進(jìn)行規(guī)則匹配:

首先是第一條規(guī)則db_trx_id == creator_trx_id,db_trx_id(4)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id < min_trx_id,db_trx_id(4)不小于min_trx_id(3)故不成立;

第三條規(guī)則db_trx_id > max_trx_id,db_trx_id(4)小于max_trx_id(6)故不成立;

第四條規(guī)則min_trx_id <= db_trx_id <= max_trx_id,db_trx_id(4)在min_trx(4)與max_trx_id(6)之間,但是同時(shí)處于m_ids(4,5)集合之中故也不成立。

經(jīng)過(guò)這次匹配,表中最新的數(shù)據(jù)無(wú)法匹配,故要與MVCC版本鏈中最上面的數(shù)據(jù)進(jìn)行規(guī)則匹配

與MVCC版本鏈中最上方的版本進(jìn)行匹配:

第一條規(guī)則db_trx_id(3)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id(3)不小于min_trx_id(4)故不成立;

第三條規(guī)則db_trx_id小于max_trx_id(6)故不成立;

第四條規(guī)則db_trx_id(3)在min_trx(3)與max_trx_id(6)之間,但是同時(shí)處于m_ids(3,4,5)集合之中故也不成立。

經(jīng)過(guò)第二次匹配,MVCC版本鏈中最上層的數(shù)據(jù)版本也無(wú)法匹配,故要與第二條版本進(jìn)行匹配

與MVCC版本鏈中第二條版本進(jìn)行匹配:

第一條規(guī)則db_trx_id(2)不等于creator_trx_id(5)故不成立;

第二條規(guī)則db_trx_id(2)小于min_trx_id(3),該版本的數(shù)據(jù)滿足匹配規(guī)則中的第二條,說(shuō)明數(shù)據(jù)已經(jīng)提交,此時(shí)匹配將終止并返回這個(gè)版本對(duì)應(yīng)的數(shù)據(jù)。

Enjoy GreatSQL :)

關(guān)于 GreatSQL

GreatSQL是由萬(wàn)里數(shù)據(jù)庫(kù)維護(hù)的MySQL分支,專注于提升MGR可靠性及性能,支持InnoDB并行查詢特性,是適用于金融級(jí)應(yīng)用的MySQL分支版本。

相關(guān)鏈接: GreatSQL社區(qū) Gitee GitHub Bilibili

GreatSQL社區(qū):

歡迎來(lái)GreatSQL社區(qū)發(fā)帖提問(wèn) https://greatsql.cn/

技術(shù)交流群:

微信:掃碼添加GreatSQL社區(qū)助手微信好友,發(fā)送驗(yàn)證信息加群。

總結(jié)

以上是生活随笔為你收集整理的图文结合带你搞懂InnoDB MVCC的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。