<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        MySQL數據庫InnoDB存儲引擎多版本控制(MVCC)實現原理分析_MySQL

        來源:懂視網 責編:小采 時間:2020-11-09 19:18:47
        文檔

        MySQL數據庫InnoDB存儲引擎多版本控制(MVCC)實現原理分析_MySQL

        MySQL數據庫InnoDB存儲引擎多版本控制(MVCC)實現原理分析_MySQL:文/何登成導讀: 來自網易研究院的MySQL內核技術研究人何登成,把MySQL數據庫InnoDB存儲引擎的多版本控制(簡稱:MVCC)實現原理,做了深入的研究與詳細的文字圖表分析,方便大家理解InnoDB存儲引擎實現的多版本控制技術(簡稱:MVCC)?;局R假設對于
        推薦度:
        導讀MySQL數據庫InnoDB存儲引擎多版本控制(MVCC)實現原理分析_MySQL:文/何登成導讀: 來自網易研究院的MySQL內核技術研究人何登成,把MySQL數據庫InnoDB存儲引擎的多版本控制(簡稱:MVCC)實現原理,做了深入的研究與詳細的文字圖表分析,方便大家理解InnoDB存儲引擎實現的多版本控制技術(簡稱:MVCC)。基本知識假設對于
        文/何登成

        導讀:

        來自網易研究院的MySQL內核技術研究人何登成,把MySQL數據庫InnoDB存儲引擎的多版本控制(簡稱:MVCC)實現原理,做了深入的研究與詳細的文字圖表分析,方便大家理解InnoDB存儲引擎實現的多版本控制技術(簡稱:MVCC)。

        基本知識

        假設對于多版本控制(MVCC)的基礎知識,有所了解。MySQL數據庫InnoDB存儲引擎為了實現多版本的一致性讀,采用的是基于回滾段的協議。

        行結構

        MySQL數據庫InnoDB存儲引擎表數據的組織方式為主鍵聚簇索引。由于采用索引組織表結構,記錄的ROWID是可變的(索引頁的時候,Structure Modification Operation,SMO),因此二級索引中采用的是(索引鍵值, 主鍵鍵值)的組合來唯一確定一條記錄。

        無論是聚簇索引,還是二級索引,其每條記錄都包含了一個DELETED BIT位,用于標識該記錄是否是刪除記錄。除此之外,聚簇索引記錄還有兩個系統列:DATA_TRX_ID,DATA_ROLL_PTR。DATA _TRX_ID表示產生當前記錄項的事務ID;DATA _ROLL_PTR指向當前記錄項的undo信息。

        聚簇索引行結構(與多版本一致讀有關的部分,DELETED BIT省略):

        innodb-mvcc-1

        二級索引行結構:

        innodb-mvcc-2

        從聚簇索引行結構,與二級索引行結構可以看出,聚簇索引中包含版本信息(事務號+回滾指針),二級索引不包含版本信息,二級索引項的可見性如何判斷?下面將會給出。

        Read View

        InnoDB存儲引擎默認的隔離級別為Repeatable Read (RR),可重復讀。InnoDB存儲引擎在開始一個RR讀之前,會創建一個Read View。Read View用于判斷一條記錄的可見性。Read View定義在read0read.h文件中,其中最主要的與可見性相關的屬性如下:

        ?
        1234567101112131415161718192021

        dulintlow_limit_id;/* 事務號 >= low_limit_id的記錄,對于當前Read View都是不可見的 */

        dulintup_limit_id;/* 事務號 < up_limit_id ,對于當前Read View都是可見的 */

        ulintn_trx_ids;/* Number of cells in the trx_ids array */

        dulint*trx_ids;/* Additional trx ids which the read should

        not see: typically, these are the active

        transactions at the time when the read is

        serialized, except the reading transaction

        itself; the trx ids in this array are in a

        descending order */

        dulintcreator_trx_id;/* trx id of creating transaction, or

        (0, 0) used in purge */

        簡單來說,Read View記錄讀開始時,所有的活動事務,這些事務所做的修改對于Read View是不可見的。除此之外,所有其他的小于創建Read View的事務號的所有記錄均可見??梢姲▋蓪雍x:

      1. 記錄可見,且Deleted bit = 0;當前記錄是可見的有效記錄。
      2. 記錄可見,且Deleted bit = 1;當前記錄是可見的刪除記錄。此記錄在本事務開始之前,已經刪除。
      3. 測試方法:

        ?
        123456710111213141516

        -create table and index

        create table test (id int primary key, comment char(50)) engine=InnoDB;

        create index test_idx on test(comment);

        -Insert

        insert into test values(1, ‘aaa’);

        insert into test values(2, ‘bbb’);

        -update primary key

        update test set id = 9 where id = 1;

        -update non-primary key with different value

        update test set comment = ‘ccc’ where id = 9;

        -update non-primary key with same value

        update test set comment = ‘bbb’ where id = 2 and comment = ‘bbb’;

        -read隔離級別

        repeatable read(RR)

        測試結果

        update primary key

        代碼調用流程:

        ?
        1ha_innobase::update_row -> row_update_for_mysql -> row_upd_step -> row_upd -> row_upd_clust_step -> row_upd_clust_rec_by_insert -> btr_cur_del_mark_set_clust_rec -> row_ins_index_entry

        簡單來說,就是將cluster index的舊記錄標記位刪除;插入一條新紀錄。該語句執行完之后,數據結構如下:

        innodb-mvcc-3-300x248

        老版本仍舊存儲在聚簇索引之中,其DATA_TRX_ID被設置為1811,Deleted bit設置為1,undo中記錄了前鏡像的事務id = 1809。新版本DATA_TRX_ID也為1811。通過此圖,還可以發現,雖然新老版本是一條記錄,但是在聚簇索引中是通過兩條記錄來標識的。同時, 由于更新了主鍵,二級索引也需要做相應的更新(二級索引中包含主鍵項)。

        update non-primary key(diff value)

        更新comment字段,代碼調用流程與上面有部分不同,可以自行跟蹤,此處省略。更新操作執行完之后,索引結構變更如下:

        innodb-mvcc-4-300x213

        從上圖可見,更新二級索引的鍵值時,聚簇索引本身并不會產生新的記錄項,而是將舊版本信息記錄在undo之中。與此同時,二級索引將會產生 新的索引項,其PK值保持不變,指向聚簇索引的同一條記錄。細心的讀者可能會發現,二級索引頁面中有一個MAX_TRX_ID,此值記錄的是更新二級索引 頁面的最大事務ID。通過MAX_TRX_ID的過濾,INNODB能夠實現大部分的輔助索引覆蓋性掃描(僅僅掃描輔助索引,不需要回聚簇索引)。具體過 濾方法,將在后面的內容中給出。

        update non-primary key(same value)

        最后一個測試用例,是更新comment項為同樣的值。在我的測試中,更新之后的索引結構如下:

        innodb-mvcc-5-300x220

        聚簇索引仍舊會更新,但是二級索引保持不變。

        總結

        1. 無論是聚簇索引,還是二級索引,只要其鍵值更新,就會產生新版本。將老版本數據deleted bti設置為1;同時插入新版本。
        2. 對于聚簇索引,如果更新操作沒有更新primary key,那么更新不會產生新版本,而是在原有版本上進行更新,老版本進入undo表空間,通過記錄上的undo指針進行回滾。
        3. 對于二級索引,如果更新操作沒有更新其鍵值,那么二級索引記錄保持不變。
        4. 對于二級索引,更新操作無論更新primary key,或者是二級索引鍵值,都會導致二級索引產生新版本數據。
        5. 聚簇索引設置記錄deleted bit時,會同時更新DATA_TRX_ID列。老版本DATA_TRX_ID進入undo表空間;二級索引設置deleted bit時,不寫入undo。

        可見性判斷

        主鍵查找

        select * from test where id = 1;

      4. 針對測試1,如果1811(DATA_TRX_ID) < read_view.up_limit_id,證明被標記為刪除的記錄1可見。刪除可見 -> 無記錄返回。
      5. 針對測試1,如果 1811(DATA_TRX_ID) >= read_view.low_limit_id,證明被標記為刪除的記錄1不可見,通過DATA_ROLL_PTR回滾記錄,得到DATA_TRX_ID = 1809。如果1809可見,則返回記錄(1,aaa);否則無記錄返回。
      6. 針對測試1,如果up_limit_id,low_limit_id都無法判斷可見性,那么遍歷read_view中的trx_ids,依次對比事務id,如果在DATA_TRX_ID在trx_ids數組中,則不可見(更新未提交)。

      7. select * from test where id = 9;

      8. 針對測試2,如果1816可見,返回(9,ccc)。
      9. 針對測試2,如果1816不可見,通過DATA_ROLL_PTR回滾到1811,如果1811可見,返回(9, aaa)。
      10. 針對測試2,如果1811不可見,無結果返回。

      11. select * from test where id > 0;

      12. 針對測試1,索引中, 滿足條件的同一記錄,有兩個版本(版本1,delete bit =1)。那么是否會一條記錄返回兩次呢?必定不會,這是因為pk = 1的可見性與pk = 9的可見性是一致的,同時pk = 1是標記了deleted bit的版本。如果事務ID = 1811可見。那么pk = 1 delete可見,無記錄返回,pk = 9返回記錄;如果1811不可見,回滾到1809可見,那么pk = 1返回記錄,pk = 9回滾后無記錄。
      13. 總結:

        1. 通過主鍵查找記錄,需要配合read_view,記錄DATA_TRX_ID,記錄DATA_ROLL_PTR指針共同判斷。
        2. read_view用于判斷當前記錄是否可見(判斷DATA_TRX_ID)。DATA_ROLL_PTR用于將當前記錄回滾到前一版本。

        非主鍵查找

        select comment from test where comment > ‘ ‘;

      14. 針對測試2,二級索 引,當前頁面的最大更新事務MAX_TRX_ID = 1816。如果MAX_TRX_ID < read_view.up_limit_id,當前頁面所有數據均可見,本頁面可以進行索引覆蓋性掃描。丟棄所有deleted bit = 1的記錄,返回deleted bit = 0 的記錄;此時返回 (ccc)。(row_select_for_mysql -> lock_sec_rec_cons_read_sees)
      15. 針對測試2,二級索 引,如果當前頁面不能滿足MAX_TRX_ID < read_view.up_limit_id,說明當前頁面無法進行索引覆蓋性掃描,此時需要針對每一項,到聚簇索引中判斷可見性。回到測試2,二級索引 中有兩項pk = 9 (一項deleted bit = 1,另一個為0),對應的聚簇索引中只有一項pk= 9。如何保證通過二級索引過來的同一記錄的多個版本,在聚簇索引中最多只能被返回一次?如果當前事務id 1811可見。二級索引pk = 9的記錄(兩項),通過聚簇索引的undo,都定位到了同一記錄項。此時,InnoDB通過以下的一個表達式,來保證來自二級索引,指向同一聚簇索引記錄 的多個版本項,有且最多僅有一個版本將會返回數據:

        ?
        1234567if (clust_rec

        && (old_vers || rec_get_deleted_flag(

        rec,dict_table_is_comp(sec_index->table)))&& !row_sel_sec_rec_is_for_clust_rec(rec, sec_index, clust_rec, clust_index))
      16. 滿足if判斷的所有聚簇索引記錄,都直接丟棄,以上判斷的邏輯如下:

        1. 需要回聚簇索引掃描,并且獲得記錄
        2. 聚簇索引記錄為回滾版本,或者二級索引中的記錄為刪除版本
        3. 聚簇索引項,與二級索引項,其鍵值并不相等

        為什么滿足if判斷,就可以直接丟棄數據?用白話來說,就是我們通過二級索引記錄,定位聚簇索引記錄,定位之后,還需要再次檢查聚簇索引記錄是否仍舊是我在二級索引中看到的記錄。如果不是,則直接丟棄;如果是,則返回。

        根據此條件,結合查詢與測試2中的索引結構。可見版本為事務1811.二級索引中的兩項pk = 9都能通過聚簇索引回滾到1811版本。但是,二級索引記錄(ccc,9)與聚簇索引回滾后的版本(aaa,9)不一致,直接丟棄。只有二級索引記錄 (aaa,9)保持一致,直接返回。

        總結:

        1. 二級索引的多版本可見性判斷,需要通過聚簇索引完成。
        2. 二級索引頁面中保存了MAX_TRX_ID,可以快速判斷當前頁面中,是否所有項均可見,可以實現二級索引頁面級別的索引覆蓋掃描。一般而言,此判斷是滿足條件的,保證了索引覆蓋掃描 (index only scan)的高效性。
        3. 二級索引中的項,需要與聚簇索引中的可見性進行比較,保證聚簇索引中的可見項,與二級索引中的項數據一致。

        疑問

        1. 在http://blogs.InnoDB.com/wp/2011/04/mysql-5-6-multi-threaded-purge/中, 作者提到,InnoDB存儲引擎的purge操作,是通過遍歷undo來實現對于標記位deleted項的回收的。如果二級索引本身標記deleted位不記錄 undo,那么這個回收操作如何完成?還是說purge是通過解析redo來完成回收的?(根據下面對于purge的流程分析,此問題已解決)

        Purge流程

        Purge功能:

        InnoDB由于要支持多版本協議,因此無論是更新,刪除,都只是設置記錄上的deleted bit標記位,而不是真正的刪除記錄。后續這些記錄的真正刪除,是通過Purge后臺進程實現的。Purge進程定期掃描InnoDB的undo,按照先 讀老undo,再讀新undo的順序,讀取每條undo record。對于每一條undo record,判斷其對應的記錄是否可以被purge(purge進程有自己的read view,等同于進程開始時最老的活動事務之前的view,保證purge的數據,一定是不可見數據,對任何人來說),如果可以purge,則構造完整記 錄(row_purge_parse_undo_rec)。然后按照先purge二級索引,最后purge聚簇索引的順序,purge一個操作生成的舊版本完整記錄。

        一個完整的purge函數調用流程如下:

        ?
        123row_purge_step->row_purge->trx_purge_fetch_next_rec->row_purge_parse_undo_rec->row_purge_del_mark->row_purge_remove_sec_if_poss->row_purge_remove_clust_if_poss

        總結:

        1. purge是通過遍歷undo實現的。
        2. purge的粒度是一條記錄上的一個操作。如果一條記錄被update了3次,產生3個old版本,均可purge。那么purge讀取undo,對于每一個操作,都會調用一次purge。一個purge刪除一個操作產生的old版本(按照操作從老到新的順序)。
        3. purge按照先二級索引,最后聚簇索引的順序進行。
        4. purge二級索引,通過構造出的索引項進行查找定位。不能直接針對某個二級頁面進行,因為不知道記錄的存放page。
        5. 對于二級索引設置deleted bit為不需要記錄undo,因為purge是根據聚簇索引undo實現。因此二級索引deleted bit被設置為1的項,沒有記錄undo,仍舊可以被purge。
        6. purge是一個耗時的操作。二級索引的purge,需要search_path定位數據,相當于每個二級索引,都做了一次index unique scan。
        7. 一次delete操作,IO翻番。第一次IO是將記錄的deleted bit設置為1;第二次的IO是將記錄刪除。

        文章具體來源不詳,如有知情者,請在評論中回復。

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        MySQL數據庫InnoDB存儲引擎多版本控制(MVCC)實現原理分析_MySQL

        MySQL數據庫InnoDB存儲引擎多版本控制(MVCC)實現原理分析_MySQL:文/何登成導讀: 來自網易研究院的MySQL內核技術研究人何登成,把MySQL數據庫InnoDB存儲引擎的多版本控制(簡稱:MVCC)實現原理,做了深入的研究與詳細的文字圖表分析,方便大家理解InnoDB存儲引擎實現的多版本控制技術(簡稱:MVCC)?;局R假設對于
        推薦度:
        標簽: 網易 數據庫 技術
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top 主站蜘蛛池模板: 亚洲高清视频在线观看| 永久亚洲成a人片777777| 亚洲欧洲日本国产| 久久九九全国免费| 色播亚洲视频在线观看| 久久国产乱子伦精品免费一| 亚洲日韩在线观看免费视频| 一级毛片视频免费| 狠狠色婷婷狠狠狠亚洲综合| 久久久精品视频免费观看| 亚洲熟妇无码八AV在线播放| 中文字幕在线免费观看视频| 亚洲国产精品成人精品无码区 | 中国极品美軳免费观看| 国产亚洲成人在线播放va| a级日本高清免费看| 亚洲午夜精品一区二区| 69式互添免费视频| 亚洲性色精品一区二区在线| 日本一道综合久久aⅴ免费| 免费人妻精品一区二区三区| 亚洲毛片αv无线播放一区| 特级精品毛片免费观看| 亚洲中文字幕久在线| 成在线人永久免费视频播放| 日本黄页网址在线看免费不卡| 亚洲人成人网站色www| 久久99精品视免费看| 亚洲综合色婷婷在线观看| 日本中文一区二区三区亚洲| 免费国产污网站在线观看| 亚洲av无码久久忘忧草| 亚洲人妻av伦理| 100000免费啪啪18免进| 日本激情猛烈在线看免费观看| 亚洲AV日韩AV永久无码下载| 永久免费av无码网站大全| 精品国产免费一区二区三区| 亚洲国产品综合人成综合网站| 亚洲成?v人片天堂网无码| 无码AV片在线观看免费|