1. MySQL之InnoDB存儲引擎:MVCC 多版本并發(fā)控制

        共 2353字,需瀏覽 5分鐘

         ·

        2022-06-20 13:36

        這里介紹InnoDB存儲引擎中如何通過MVCC 多版本并發(fā)控制實(shí)現(xiàn)不同隔離級別下的查詢

        abstract.png

        基本原理

        版本鏈

        在InnoDB存儲引擎下,對于聚簇索引中的記錄而言,其會含有兩個必要的隱藏列:trx_id、roll_pointer。一方面,當(dāng)事務(wù)對聚簇索引中的記錄進(jìn)行改動時,即會把該事務(wù)ID賦值給trx_id列;另一方面,每次對聚簇索引進(jìn)行改動后,其會將該記錄舊版本寫入到undo日志當(dāng)中。故roll_pointer列本質(zhì)上就是一個指針,用于找到該聚簇索引改動前的版本。這樣每次對記錄進(jìn)行改動即會形成一條相應(yīng)的undo日志記錄,同樣地,在每條undo日志記錄也會包含生成該版本時對應(yīng)的事務(wù)ID信息。同時對于每個undo日志記錄也有一個roll_pointer屬性。使得undo日志記錄之間可以形成一個鏈表,如下所示。可以看到,對于一條記錄的多個歷史版本形成了一個版本鏈

        figure 1.jpeg

        ReadView

        前面已經(jīng)看到通過版本鏈可以記錄下各事務(wù)對記錄的修改結(jié)果。而為了實(shí)現(xiàn)在不同隔離級別條件下,能夠在版本鏈中找到合適的版本以對當(dāng)前事務(wù)可見。InnoDB引入了ReadView的概念,其包含以下重要信息

        • 「m_ids」:在生成ReadView時,當(dāng)前數(shù)據(jù)庫中活躍的讀寫事務(wù)的事務(wù)ID列表
        • 「min_trx_id」:在生成ReadView時,當(dāng)前活躍的讀寫事務(wù)中最小的事務(wù)ID,也就是m_ids中的最小值
        • 「max_trx_id」:在生成ReadView時,系統(tǒng)中下一次給其他事務(wù)分配所使用的ID。這里對事務(wù)ID進(jìn)行補(bǔ)充說明,一方面,事務(wù)ID的分配保證全局遞增;另一方面,只有在對記錄進(jìn)行修改時(執(zhí)行INSERT、DELETE、UPDATE語句)才會為該事務(wù)分配事務(wù)ID。否則對于一個只讀事務(wù)而言,其事務(wù)ID使用默認(rèn)值0
        • 「creator_trx_id」:生成該ReadView的事務(wù)所對應(yīng)的事務(wù)ID

        至此,我們就可以利用當(dāng)前事務(wù)所生成的ReadView來判斷版本鏈中某個版本的記錄,是否對當(dāng)前事務(wù)可見。具體地規(guī)則如下:

        • 如果被訪問版本的trx_id屬性值 等于 ReadView中的creator_trx_id值。意味著當(dāng)前事務(wù)在訪問它自己修改過的記錄,所以該版本可以被當(dāng)前事務(wù)訪問
        • 如果被訪問版本的trx_id屬性值 小于 ReadView中的min_trx_id值。表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView前已經(jīng)提交,所以該版本可以被當(dāng)前事務(wù)訪問
        • 如果被訪問版本的trx_id屬性值 大于等于 ReadView中的max_trx_id值。表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView后才開啟,所以該版本不可以被當(dāng)前事務(wù)訪問
        • 如果被訪問版本的trx_id屬性值 在ReadView的min_trx_id和max_trx_id之間。即 min_trx_id ≤ 被訪問版本的trx_id屬性值 < max_trx_id。則我們需要進(jìn)一步判斷 被訪問版本的trx_id屬性值 是不是在 m_ids列表 中。如果是,則說明創(chuàng)建ReadView時,生成該版本的事務(wù)還是活躍的,則該版本不可以被訪問;反之,則說明創(chuàng)建ReadView時,生成該版本的事務(wù)已經(jīng)被提交,故該版本可以被訪問

        在查詢記錄時,通過版本鏈確定版本的可見性。如果該版本可見則表示版本確定完畢,返回該版本的數(shù)據(jù)作為查詢結(jié)果;反之,如果某個版本的數(shù)據(jù)對當(dāng)前事務(wù)不可見的話,則根據(jù)版本鏈找到下一個版本的數(shù)據(jù),重復(fù)使用上述的規(guī)則進(jìn)行判斷,依此類推。如果版本鏈中最后一個版本也不可見的話,則說明該條數(shù)據(jù)記錄對該當(dāng)前事務(wù)完全不可見,即查詢結(jié)果不包含該記錄

        隔離級別

        Read Uncommitted 未提交讀

        對于隔離級別為 Read Uncommitted 未提交讀 的事務(wù)而言。由于允許讀到其他未提交事務(wù)修改過的記錄。故直接讀取記錄的最新版本即可。無需使用MVCC機(jī)制

        Read Committed 已提交讀

        對于隔離級別為 Read Committed 已提交讀 的事務(wù)而言,由于允許讀到其他已提交事務(wù)修改過的記錄。故在該事務(wù)當(dāng)中,其每次執(zhí)行select查詢語句時都會重新創(chuàng)建一個新的ReadView用于進(jìn)行版本的可見性判斷

        Repeatable Read 可重復(fù)讀

        對于隔離級別為 Repeatable Read 可重復(fù)讀 的事務(wù)而言,其要求該事務(wù)中無論進(jìn)行多少次查詢,對于某條記錄而言,其數(shù)據(jù)內(nèi)容均不能發(fā)生改變。故在該事務(wù)當(dāng)中,其只會在第一次執(zhí)行select查詢語句時創(chuàng)建ReadView。后續(xù)該事務(wù)再次執(zhí)行select查詢語句時,將不會重新創(chuàng)建ReadView。而是會利用該事務(wù)第一次創(chuàng)建的ReadView進(jìn)行版本可見性的判斷

        Serializable 串行化

        對于隔離級別為 Serializable 串行化 的事務(wù)而言。InnoDB規(guī)定其讀取數(shù)據(jù)采用加鎖的方式進(jìn)行實(shí)現(xiàn),而不使用MVCC機(jī)制

        Note

        1. 事實(shí)上,對于trx_id、roll_pointer隱藏列而言,其真實(shí)的名稱為DB_TRX_ID、DB_ROLL_PTR。本文只是為了行文方便換了個叫法
        2. 特別地,對于二級索引而言。雖然其沒有trx_id隱藏列。但在二級索引頁面的Page Header中有一個PAGE_MAX_TRX_ID屬性,用于表示對該頁面進(jìn)行改動的最大事務(wù)ID。如果PAGE_MAX_TRX_ID屬性值 小于 當(dāng)前活躍的讀寫事務(wù)中最小的事務(wù)ID,則說明對該頁面做修改的事務(wù)都已經(jīng)提交了,即是可見的;反之,則需要進(jìn)行回表,在聚簇索引中通過MVCC機(jī)制判斷可見性

        參考文獻(xiàn)

        1. MySQL是怎樣運(yùn)行的:從根兒上理解MySQL 小孩子4919著
        瀏覽 67
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 想要xx在线 | 美女粉嫩b | 国产又粗又黄又爽的视频 | 91靠逼视频 | 久久精品片 |