1.mvcc概要
指Multi-Version Concurrency Control,多版本并發(fā)控制。
- 設(shè)計目的:解決讀-寫并發(fā)問題稼稿。注意是讀-寫并發(fā),因為一般來說讳窟,一個數(shù)據(jù)如果正被寫入让歼,寫操作是比較耗時的,那么此時讀操作是需要阻塞的丽啡,得等這個寫入結(jié)束了才能讀谋右。mvcc就是用來實現(xiàn)寫數(shù)據(jù)的同時能夠并發(fā)讀的目的。但寫-寫仍然是互斥的补箍。
- 實現(xiàn)機制:從本質(zhì)上講改执,對【一份數(shù)據(jù)】做到讀寫并發(fā)肯定是不可能的啸蜜,既然一份數(shù)據(jù)搞不定,那就用多份數(shù)據(jù)去做辈挂。數(shù)據(jù)只有一份衬横,而且正在被修改,這個時候來一個讀請求终蒂,你讓它怎么讀蜂林?讀這個狀態(tài)不穩(wěn)定隨時會變化的數(shù)據(jù)?no拇泣。既然一份數(shù)據(jù)做不到讀-寫并發(fā)悉尾,那搞幾個數(shù)據(jù)副本嘛。不管讀還是寫挫酿,都在“自己可見的數(shù)據(jù)副本”上操作,互不影響愕难。這里的副本就是Multi-Version早龟。
- mvcc工作的事務(wù)隔離級別:read committed, read repearable
2.mvcc的具體實現(xiàn)
mvcc主要依賴隱藏字段,ReadView和undo log實現(xiàn)
2.1 隱藏字段
隱藏字段的主要功能是猫缭,記錄最后一次修改數(shù)據(jù)的事務(wù)葱弟,并且可以追溯歷史版本的數(shù)據(jù)
Internally, InnoDB adds three fields to each row stored in the database:
A 6-byte DB_TRX_ID field indicates the transaction identifier for the last transaction that inserted or updated the row. Also, a deletion is treated internally as an update where a special bit in the row is set to mark it as deleted.
A 7-byte DB_ROLL_PTR field called the roll pointer. The roll pointer points to an undo log record written to the rollback segment. If the row was updated, the undo log record contains the information necessary to rebuild the content of the row before it was updated.
A 6-byte DB_ROW_ID field contains a row ID that increases monotonically as new rows are inserted. If InnoDB generates a clustered index automatically, the index contains row ID values. Otherwise, the DB_ROW_ID column does not appear in any index.
以上介紹來源于MySQL8.0 InnoDB Multi-Versioning
這說明,MySQL8.0里面對每行數(shù)據(jù)加入的隱藏字段有3個:
- 最后寫事務(wù)的ID: DB_TRX_ID
- 指向歷史數(shù)據(jù)的回滾指針: DB_ROLL_PTR
-
數(shù)據(jù)行號: DB_ROW_ID
其中猜丹,比較重要的是DB_TRX_ID和DB_ROLL_PTR
2.2 ReadView
2.2.1 ReadView基本概念
ReadView芝加,讀視圖,決定了哪些版本的數(shù)據(jù)對事務(wù)可見射窒。換句話說藏杖,事務(wù)是依據(jù)ReadView選擇可見數(shù)據(jù)的,ReadView看得見什么脉顿,事務(wù)就看得見什么蝌麸。ReadView是不同事務(wù)間實現(xiàn)多版本數(shù)據(jù)的根本,我們討論哪些數(shù)據(jù)對特定事務(wù)可見艾疟,其實是在討論哪些數(shù)據(jù)對特定的ReadView可見
2.2.2 ReadView的產(chǎn)生
注意:事務(wù)begin不生成ReadView来吩,一個事務(wù)的ReadView在事務(wù)內(nèi)【執(zhí)行普通select】操作時生成。而且蔽莱,只有普通select語句才會創(chuàng)建ReadView弟疆,select ... lock in share mode,select ... for update不會盗冷,update怠苔、delete、insert語句也不會仪糖,因為它們都是當(dāng)前讀嘀略,會對訪問的數(shù)據(jù)加鎖
以多事務(wù)的update為例:
其中恤溶,// number表示命令的執(zhí)行順序≈难颍可以看到咒程,// 2的update給數(shù)據(jù)上鎖之后,// 3就會一直等待
讀提交和可重復(fù)讀讼育,實際上就是通過ReadView的不同機制實現(xiàn)的帐姻。
- 在讀提交隔離級別中,事務(wù)內(nèi)每次普通select都即時產(chǎn)生一個ReadView奶段,因此可以讀到別的事務(wù)已經(jīng)提交的數(shù)據(jù)饥瓷;
- 而在可重復(fù)讀隔離級別中,事務(wù)的首次普通select產(chǎn)生一個ReadView痹籍,并且這個ReadView用于事務(wù)內(nèi)后續(xù)所有的普通select呢铆,從而實現(xiàn)可重復(fù)讀
2.2.3 ReadView的數(shù)據(jù)結(jié)構(gòu)
下面來看一下ReadView究竟長什么樣
ReadView主要依賴以下幾個字段:
- creator_trx_id 當(dāng)前創(chuàng)建的事務(wù)ID,全局遞增《撞現(xiàn)在設(shè)當(dāng)前創(chuàng)建的事務(wù)為T棺克,首次select產(chǎn)生的ReadView為rv
- up_limit_id 在事務(wù)T產(chǎn)生rv的那一時刻,距離事務(wù)T的【最遠(yuǎn)】【活躍】事務(wù)ID线定。最遠(yuǎn)即事務(wù)編號最小娜谊。
- low_limit_id 在事務(wù)T產(chǎn)生rv的那一時刻,【全局】事務(wù)范圍內(nèi)【下一個】將被分配的事務(wù)ID斤讥,也就是未被分配的最小事務(wù)編號纱皆。例如,此刻全局的所有事務(wù)已經(jīng)編號到999了芭商,那么1000就作為rv的low_limit_id派草。
- trx_ids。up_limit_id和low_limit_id是左閉右開的關(guān)系铛楣,即[up_limit_id, low_limit_id)澳眷,我們把這個區(qū)間內(nèi)的除事務(wù)T自己外的活動事務(wù)記為trx_ids,表示事務(wù)T的rv視圖不可見的其他活躍事務(wù)蛉艾。
ReadView的核心是trx_ids钳踊。
遍歷所有record,記遍歷到的當(dāng)前record的DB_TRX_ID為t_id
- if t_id out of [up_limit_id, low_limit_id)
要么是id < up_limit_id勿侯,即修改當(dāng)前record的事務(wù)已經(jīng)commit拓瞪,顯然已完成事務(wù)的數(shù)據(jù)更改是對rv可見的;
要么是id >= low_limit_id助琐,即在rv產(chǎn)生后才創(chuàng)建的事務(wù)祭埂,這些事務(wù)對record的修改在rv的未來發(fā)生,對rv不可見,那么就順著record的DB_ROLL_PTR一直找到rv可見的歷史版本數(shù)據(jù)
- else up_limit_id <= t_id < low_limit_id
此時如果t_id in trx_ids蛆橡,就表明最后修改該record的事務(wù)在rv創(chuàng)建時處于活躍狀態(tài)中舌界,也就是說t_id和T在rv創(chuàng)建時刻是并行活躍的,出于事務(wù)間的獨立性泰演,該record對rv不可見呻拌,那么就順著record的DB_ROLL_PTR一直找到rv可見的歷史版本數(shù)據(jù)
否則,t_id not in trx_ids睦焕,說明在rv產(chǎn)生的時候藐握,最后修改該record的事務(wù)早已經(jīng)提交了,因此record對rv可見
2.3 Undo log
Undo log以鏈表形式存儲了歷史版本數(shù)據(jù)垃喊,如果當(dāng)前記錄行對事務(wù)不可見猾普,需要順著undo log鏈找到滿足其可見性條件的記錄行版本
3. 例子
本圖例來自MySQL中MVCC的正確打開方式(源碼佐證),本文整理的內(nèi)容也參考了其中的內(nèi)容本谜。還是要多看官方文檔初家,多看源碼,比啥都強
--
補充:
個人理解乌助,mvcc最樸素的思想還是【一寫多讀】溜在,寫操作只有一個版本,而讀操作可以有多個版本眷茁。類似針對數(shù)據(jù)的“主寫從讀”思想,一主(最新版本數(shù)據(jù)纵诞,用于寫)多從(多份歷史版本數(shù)據(jù)上祈,用于讀)