MVCC多版本并發(fā)控制

1、MVCC

MVCC,全稱Multi-Version Concurrency Control搜囱,即多版本并發(fā)控制。MVCC是一種并發(fā)控制的方法柑土,一般在數(shù)據(jù)庫管理系統(tǒng)中蜀肘,實現(xiàn)對數(shù)據(jù)庫的并發(fā)訪問,在編程語言中實現(xiàn)事務(wù)內(nèi)存稽屏。

MVCC在MySQL InnoDB中的實現(xiàn)主要是為了提高數(shù)據(jù)庫并發(fā)性能扮宠,用更好的方式去處理讀寫沖突,做到即使有讀寫沖突時诫欠,也能做到不加鎖涵卵,非阻塞并發(fā)讀浴栽。

2、當(dāng)前讀

像select lock in share mode(共享鎖), select for update ; update, insert ,delete(排他鎖)這些操作都是一種當(dāng)前讀轿偎,為什么叫當(dāng)前讀典鸡?就是它讀取的是記錄的最新版本,讀取時還要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄坏晦,會對讀取的記錄進行加鎖萝玷。

3、快照讀(提高數(shù)據(jù)庫的并發(fā)查詢能力)

像不加鎖的select操作就是快照讀昆婿,即不加鎖的非阻塞讀球碉;快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當(dāng)前讀仓蛆;之所以出現(xiàn)快照讀的情況睁冬,是基于提高并發(fā)性能的考慮,快照讀的實現(xiàn)是基于多版本并發(fā)控制看疙,即MVCC,可以認(rèn)為MVCC是行鎖的一個變種豆拨,但它在很多情況下,避免了加鎖操作能庆,降低了開銷施禾;既然是基于多版本,即快照讀可能讀到的并不一定是數(shù)據(jù)的最新版本搁胆,而有可能是之前的歷史版本

4弥搞、當(dāng)前讀、快照讀渠旁、MVCC關(guān)系

MVCC多版本并發(fā)控制指的是維持一個數(shù)據(jù)的多個版本攀例,使得讀寫操作沒有沖突,快照讀是MySQL為實現(xiàn)MVCC的一個非阻塞讀功能一死。MVCC模塊在MySQL中的具體實現(xiàn)是由三個隱式字段肛度,undo日志、read view三個組件來實現(xiàn)的投慈。

5承耿、MVCC解決的問題

數(shù)據(jù)庫并發(fā)場景有三種,分別為:

1伪煤、讀讀:不存在任何問題加袋,也不需要并發(fā)控制

2、讀寫:有線程安全問題抱既,可能會造成事務(wù)隔離性問題职烧,可能遇到臟讀、幻讀、不可重復(fù)讀

3蚀之、寫寫:有線程安全問題蝗敢,可能存在更新丟失問題

MVCC是一種用來解決讀寫沖突的無鎖并發(fā)控制,也就是為事務(wù)分配單項增長的時間戳足删,為每個修改保存一個版本寿谴,版本與事務(wù)時間戳關(guān)聯(lián),讀操作只讀該事務(wù)開始前的數(shù)據(jù)庫的快照失受,所以MVCC可以為數(shù)據(jù)庫解決以下問題:

1讶泰、在并發(fā)讀寫數(shù)據(jù)庫時,可以做到在讀操作時不用阻塞寫操作拂到,寫操作也不用阻塞讀操作痪署,提高了數(shù)據(jù)庫并發(fā)讀寫的性能

2、解決臟讀兄旬、幻讀狼犯、不可重復(fù)讀等事務(wù)隔離問題,但是不能解決更新丟失問題

6辖试、MVCC實現(xiàn)原理

mvcc的實現(xiàn)原理主要依賴于記錄中的三個隱藏字段辜王,undolog,read view來實現(xiàn)的罐孝。

隱藏字段

每行記錄除了我們自定義的字段外,還有數(shù)據(jù)庫隱式定義的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段

DB_TRX_ID

6字節(jié)肥缔,最近修改事務(wù)id莲兢,記錄創(chuàng)建這條記錄或者最后一次修改該記錄的事務(wù)id

DB_ROLL_PTR

7字節(jié),回滾指針续膳,指向這條記錄的上一個版本,用于配合undolog改艇,指向上一個舊版本

DB_ROW_JD

6字節(jié),隱藏的主鍵坟岔,如果數(shù)據(jù)表沒有主鍵谒兄,那么innodb會自動生成一個6字節(jié)的row_id

記錄如圖所示:

在上圖中,DB_ROW_ID是數(shù)據(jù)庫默認(rèn)為該行記錄生成的唯一隱式主鍵社付,DB_TRX_ID是當(dāng)前操作該記錄的事務(wù)ID承疲,DB_ROLL_PTR是一個回滾指針,用于配合undo日志鸥咖,指向上一個舊版本

undo log

undolog被稱之為回滾日志燕鸽,表示在進行insert,delete啼辣,update操作的時候產(chǎn)生的方便回滾的日志

當(dāng)進行insert操作的時候啊研,產(chǎn)生的undolog只在事務(wù)回滾的時候需要,并且在事務(wù)提交之后可以被立刻丟棄

當(dāng)進行update和delete操作的時候,產(chǎn)生的undolog不僅僅在事務(wù)回滾的時候需要党远,在快照讀的時候也需要削解,所以不能隨便刪除,只有在快照讀或事務(wù)回滾不涉及該日志時沟娱,對應(yīng)的日志才會被purge線程統(tǒng)一清除(當(dāng)數(shù)據(jù)發(fā)生更新和刪除操作的時候都只是設(shè)置一下老記錄的deleted_bit氛驮,并不是真正的將過時的記錄刪除,因為為了節(jié)省磁盤空間花沉,innodb有專門的purge線程來清除deleted_bit為true的記錄柳爽,如果某個記錄的deleted_id為true,并且DB_TRX_ID相對于purge線程的read view 可見碱屁,那么這條記錄一定時可以被清除的)

下面我們來看一下undolog生成的記錄鏈

1磷脯、假設(shè)有一個事務(wù)編號為1的事務(wù)向表中插入一條記錄,那么此時行數(shù)據(jù)的狀態(tài)為:

2娩脾、假設(shè)有第二個事務(wù)編號為2對該記錄的name做出修改赵誓,改為lisi

在事務(wù)2修改該行記錄數(shù)據(jù)時,數(shù)據(jù)庫會對該行加排他鎖

然后把該行數(shù)據(jù)拷貝到undolog中柿赊,作為 舊記錄俩功,即在undolog中有當(dāng)前行的拷貝副本

拷貝完畢后,修改該行name為lisi碰声,并且修改隱藏字段的事務(wù)id為當(dāng)前事務(wù)2的id诡蜓,回滾指針指向拷貝到undolog的副本記錄中

事務(wù)提交后,釋放鎖

3胰挑、假設(shè)有第三個事務(wù)編號為3對該記錄的age做了修改蔓罚,改為32

在事務(wù)3修改該行數(shù)據(jù)的時,數(shù)據(jù)庫會對該行加排他鎖

然后把該行數(shù)據(jù)拷貝到undolog中瞻颂,作為舊紀(jì)錄豺谈,發(fā)現(xiàn)該行記錄已經(jīng)有undolog了,那么最新的舊數(shù)據(jù)作為鏈表的表頭贡这,插在該行記錄的undolog最前面

修改該行age為32歲茬末,并且修改隱藏字段的事務(wù)id為當(dāng)前事務(wù)3的id,回滾指針指向剛剛拷貝的undolog的副本記錄

事務(wù)提交盖矫,釋放鎖


從上述的一系列圖中丽惭,大家可以發(fā)現(xiàn),不同事務(wù)或者相同事務(wù)的對同一記錄的修改炼彪,會導(dǎo)致該記錄的undolog生成一條記錄版本線性表课舍,即鏈表逮矛,undolog的鏈?zhǔn)拙褪亲钚碌呐f記錄厘肮,鏈尾就是最早的舊記錄毅弧。

Read View

上面的流程如果看明白了局义,那么大家需要再深入理解下read view的概念了。

Read View是事務(wù)進行快照讀操作的時候生產(chǎn)的讀視圖冗疮,在該事務(wù)執(zhí)行快照讀的那一刻萄唇,會生成一個數(shù)據(jù)系統(tǒng)當(dāng)前的快照,記錄并維護系統(tǒng)當(dāng)前活躍事務(wù)的id术幔,事務(wù)的id值是遞增的另萤。

其實Read View的最大作用是用來做可見性判斷的,也就是說當(dāng)某個事務(wù)在執(zhí)行快照讀的時候诅挑,對該記錄創(chuàng)建一個Read View的視圖四敞,把它當(dāng)作條件去判斷當(dāng)前事務(wù)能夠看到哪個版本的數(shù)據(jù),有可能讀取到的是最新的數(shù)據(jù)拔妥,也有可能讀取的是當(dāng)前行記錄的undolog中某個版本的數(shù)據(jù)

Read View遵循的可見性算法主要是將要被修改的數(shù)據(jù)的最新記錄中的DB_TRX_ID(當(dāng)前事務(wù)id)取出來忿危,與系統(tǒng)當(dāng)前其他活躍事務(wù)的id去對比,如果DB_TRX_ID跟Read View的屬性做了比較没龙,不符合可見性铺厨,那么就通過DB_ROLL_PTR回滾指針去取出undolog中的DB_TRX_ID做比較,即遍歷鏈表中的DB_TRX_ID硬纤,直到找到滿足條件的DB_TRX_ID,這個DB_TRX_ID所在的舊記錄就是當(dāng)前事務(wù)能看到的最新老版本數(shù)據(jù)解滓。

Read View的可見性規(guī)則如下所示:

首先要知道Read View中的三個全局屬性:

trx_list:一個數(shù)值列表,用來維護Read View生成時刻系統(tǒng)正活躍的事務(wù)ID(1,2,3)

up_limit_id:記錄trx_list列表中事務(wù)ID最小的ID(1)

low_limit_id:Read View生成時刻系統(tǒng)尚未分配的下一個事務(wù)ID筝家,(4)

具體的比較規(guī)則如下:

1洼裤、首先比較DB_TRX_ID < up_limit_id,如果小于,則當(dāng)前事務(wù)能看到DB_TRX_ID所在的記錄溪王,如果大于等于進入下一個判斷

2逸邦、接下來判斷DB_TRX_ID >= low_limit_id,如果大于等于則代表DB_TRX_ID所在的記錄在Read View生成后才出現(xiàn)的,那么對于當(dāng)前事務(wù)肯定不可見在扰,如果小于,則進入下一步判斷

3雷客、判斷DB_TRX_ID是否在活躍事務(wù)中芒珠,如果在,則代表在Read View生成時刻搅裙,這個事務(wù)還是活躍狀態(tài)皱卓,還沒有commit,修改的數(shù)據(jù)部逮,當(dāng)前事務(wù)也是看不到娜汁,如果不在,則說明這個事務(wù)在Read View生成之前就已經(jīng)開始commit兄朋,那么修改的結(jié)果是能夠看見的掐禁。

7、MVCC的整體處理流程

從上述表格中,我們可以看到傅事,當(dāng)事務(wù)2對某行數(shù)據(jù)執(zhí)行了快照讀缕允,數(shù)據(jù)庫為該行數(shù)據(jù)生成一個Read View視圖,可以看到事務(wù)1和事務(wù)3還在活躍狀態(tài)蹭越,事務(wù)4在事務(wù)2快照讀的前一刻提交了更新障本,所以,在Read View中記錄了系統(tǒng)當(dāng)前活躍事務(wù)1响鹃,3驾霜,維護在一個列表中。同時可以看到up_limit_id的值為1买置,而low_limit_id為5粪糙,如下圖所示:

在上述的例子中,只有事務(wù)4修改過該行記錄堕义,并在事務(wù)2進行快照讀前猜旬,就提交了事務(wù),所以該行當(dāng)前數(shù)據(jù)的undolog如下所示:

當(dāng)事務(wù)2在快照讀該行記錄的是倦卖,會拿著該行記錄的DB_TRX_ID去跟up_limit_id,lower_limit_id和活躍事務(wù)列表進行比較洒擦,判讀事務(wù)2能看到該行記錄的版本是哪個。

具體流程如下:先拿該行記錄的事務(wù)ID(4)去跟Read View中的up_limit_id相比較怕膛,判斷是否小于熟嫩,通過對比發(fā)現(xiàn)不小于,所以不符合條件褐捻,繼續(xù)判斷4是否大于等于low_limit_id,通過比較發(fā)現(xiàn)也不大于掸茅,所以不符合條件,判斷事務(wù)4是否處理trx_list列表中柠逞,發(fā)現(xiàn)不再次列表中昧狮,那么符合可見性條件,所以事務(wù)4修改后提交的最新結(jié)果對事務(wù)2 的快照是可見的板壮,因此逗鸣,事務(wù)2讀取到的最新數(shù)據(jù)記錄是事務(wù)4所提交的版本,而事務(wù)4提交的版本也是全局角度的最新版本绰精。如下圖所示:


當(dāng)上述的內(nèi)容都看明白了的話撒璧,那么大家就應(yīng)該能夠搞清楚這幾個核心概念之間的關(guān)系了,下面我們講一個不同的隔離級別下的快照讀的不同笨使。

8卿樱、RC、RR級別下的InnoDB快照讀有什么不同

因為Read View生成時機的不同硫椰,從而造成RC繁调、RR級別下快照讀的結(jié)果的不同

1萨蚕、在RR級別下的某個事務(wù)的對某條記錄的第一次快照讀會創(chuàng)建一個快照即Read View,將當(dāng)前系統(tǒng)活躍的其他事務(wù)記錄起來,此后在調(diào)用快照讀的時候涉馁,還是使用的是同一個Read View,所以只要當(dāng)前事務(wù)在其他事務(wù)提交更新之前使用過快照讀门岔,那么之后的快照讀使用的都是同一個Read View,所以對之后的修改不可見

2、在RR級別下烤送,快照讀生成Read View時寒随,Read View會記錄此時所有其他活動和事務(wù)的快照,這些事務(wù)的修改對于當(dāng)前事務(wù)都是不可見的帮坚,而早于Read View創(chuàng)建的事務(wù)所做的修改均是可見

3妻往、在RC級別下,事務(wù)中试和,每次快照讀都會新生成一個快照和Read View,這就是我們在RC級別下的事務(wù)中可以看到別的事務(wù)提交的更新的原因讯泣。

總結(jié):在RC隔離級別下,是每個快照讀都會生成并獲取最新的Read View,而在RR隔離級別下阅悍,則是同一個事務(wù)中的第一個快照讀才會創(chuàng)建Read View好渠,之后的快照讀獲取的都是同一個Read View.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市节视,隨后出現(xiàn)的幾起案子拳锚,更是在濱河造成了極大的恐慌,老刑警劉巖寻行,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霍掺,死亡現(xiàn)場離奇詭異,居然都是意外死亡拌蜘,警方通過查閱死者的電腦和手機杆烁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來简卧,“玉大人兔魂,你說我怎么就攤上這事【倜洌” “怎么了入热?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晓铆。 經(jīng)常有香客問我,道長绰播,這世上最難降的妖魔是什么骄噪? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蠢箩,結(jié)果婚禮上链蕊,老公的妹妹穿的比我還像新娘事甜。我一直安慰自己,他們只是感情好滔韵,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布逻谦。 她就那樣靜靜地躺著,像睡著了一般陪蜻。 火紅的嫁衣襯著肌膚如雪邦马。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天宴卖,我揣著相機與錄音滋将,去河邊找鬼。 笑死症昏,一個胖子當(dāng)著我的面吹牛随闽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肝谭,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼掘宪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了攘烛?” 一聲冷哼從身側(cè)響起魏滚,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎医寿,沒想到半個月后栏赴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡靖秩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年须眷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沟突。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡花颗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惠拭,到底是詐尸還是另有隱情扩劝,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布职辅,位于F島的核電站棒呛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏域携。R本人自食惡果不足惜簇秒,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秀鞭。 院中可真熱鬧趋观,春花似錦扛禽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至剩辟,卻和暖如春掐场,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抹沪。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工刻肄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人融欧。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓敏弃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親噪馏。 傳聞我的和親對象是個殘疾皇子麦到,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容