淺談MySQL中的MVCC

上一篇我們簡(jiǎn)單聊了聊MySQL中LRU算法的實(shí)現(xiàn),那么這一篇我們聊聊MySQL的另一個(gè)重點(diǎn)——MVCC(多版本并發(fā)控制)嘹黔;


一峭状、什么是MVCC


???? MVCC,全稱Multi-Version Concurrency Control泊窘,即多版本并發(fā)控制熄驼。MVCC是一種并發(fā)控制的方法像寒,一般在數(shù)據(jù)庫管理系統(tǒng)中,實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的并發(fā)訪問瓜贾,在編程語言中實(shí)現(xiàn)事務(wù)內(nèi)存诺祸。


? ? MVCC在MySQL InnoDB中的實(shí)現(xiàn)主要是為了提高數(shù)據(jù)庫并發(fā)性能,用更好的方式去處理讀-寫沖突祭芦,做到即使有讀寫沖突時(shí)筷笨,也能做到不加鎖,非阻塞并發(fā)讀龟劲。


????舉個(gè)例子胃夏,程序員A正在讀數(shù)據(jù)庫中某些內(nèi)容,而程序員B正在給這些內(nèi)容做修改(假設(shè)是在一個(gè)事務(wù)內(nèi)修改昌跌,大概持續(xù)10s左右)仰禀,A在這10s內(nèi) 則可能看到一個(gè)不一致的數(shù)據(jù),在B沒有提交前蚕愤,如何讓A能夠一直讀到的數(shù)據(jù)都是一致的呢


有幾種處理方法:

????第一種:基于鎖的并發(fā)控制答恶,程序員B開始修改數(shù)據(jù)時(shí),給這些數(shù)據(jù)加上鎖审胸,程序員A這時(shí)再讀亥宿,就發(fā)現(xiàn)讀取不了,處于等待情況砂沛,只能等B操作完才能讀數(shù)據(jù)烫扼,這保證A不會(huì)讀到一個(gè)不一致的數(shù)據(jù),但是這個(gè)會(huì)影響程序的運(yùn)行效率碍庵。

????還有一種就是:MVCC映企,每個(gè)用戶連接數(shù)據(jù)庫時(shí),看到的都是某一特定時(shí)刻的數(shù)據(jù)庫快照静浴,在B的事務(wù)沒有提交之前堰氓,A始終讀到的是某一特定時(shí)刻的數(shù)據(jù)庫快照,不會(huì)讀到B事務(wù)中的數(shù)據(jù)修改情況苹享,直到B事務(wù)提交双絮,才會(huì)讀取B的修改內(nèi)容。


二得问、什么是當(dāng)前讀和快照讀


在學(xué)習(xí)MVCC多版本并發(fā)控制之前囤攀,我們必須先了解一下,什么是MySQL InnoDB下的當(dāng)前讀和快照讀?

????當(dāng)前讀:

? ? ? ? 像select 語句 :lock in share mode(共享鎖), select 語句 for update ; update, insert ,delete(排他鎖)這些操作都是一種當(dāng)前讀宫纬,為什么叫當(dāng)前讀焚挠?就是它讀取的是記錄的最新版本,讀取時(shí)還要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄漓骚,會(huì)對(duì)讀取的記錄進(jìn)行加鎖蝌衔。


? ? 快照讀:

? ? ? ? 像不加鎖的select * from 操作就是快照讀榛泛,即不加鎖的非阻塞讀,不涉及其他鎖之間的沖突噩斟;快照讀的前提是隔離級(jí)別不是串行級(jí)別曹锨,串行級(jí)別下的快照讀會(huì)退化成當(dāng)前讀;之所以出現(xiàn)快照讀的情況剃允,是基于提高并發(fā)性能的考慮艘希,快照讀的實(shí)現(xiàn)是基于多版本并發(fā)控制,即MVCC,可以認(rèn)為MVCC是行鎖的一個(gè)變種硅急,但它在很多情況下,避免了加鎖操作佳遂,降低了開銷营袜;既然是基于多版本,即快照讀可能讀到的并不一定是數(shù)據(jù)的最新版本丑罪,而有可能是之前的歷史版本荚板。

?????說白了MVCC就是為了實(shí)現(xiàn)讀(select)-寫沖突不加鎖,而這個(gè)讀指的就是快照讀, 而非當(dāng)前讀吩屹,當(dāng)前讀實(shí)際上是一種加鎖的操作跪另,是悲觀鎖的實(shí)現(xiàn)。


三煤搜、MVCC的實(shí)現(xiàn)原理

????MVCC的目的就是多版本并發(fā)控制免绿,在數(shù)據(jù)庫中的實(shí)現(xiàn),就是為了解決讀寫沖突擦盾,它的實(shí)現(xiàn)原理主要是依賴記錄中的 3個(gè)隱式字段嘲驾,undo日志 ,Read View 來實(shí)現(xiàn)的迹卢。所以我們先來看看這個(gè)三個(gè)point的概念:

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

    • ?DB_TRX_ID:? ? 6byte誊垢,最近修改(修改/插入)事務(wù)ID:記錄創(chuàng)建這條記錄/最后一次修改該記錄的事務(wù)ID

    • DB_ROLL_PTR:? ?7byte,回滾指針症见,指向這條記錄的上一個(gè)版本(存儲(chǔ)于rollback segment里)

    • DB_ROW_ID:? ? ? ? 6byte喂走,隱含的自增ID(隱藏主鍵),如果數(shù)據(jù)表沒有主鍵筒饰,InnoDB會(huì)自動(dòng)以DB_ROW_ID產(chǎn)生一個(gè)聚集索引

    undo log日志主要分為兩種:

    ????

        • insert undo log:?代表事務(wù)在insert新記錄時(shí)產(chǎn)生的undo log, 只在事務(wù)回滾時(shí)需要缴啡,并且在事務(wù)提交后可以被立即丟棄


        • update undo log:? 事務(wù)在進(jìn)行update或delete時(shí)產(chǎn)生的undo log; 不僅在事務(wù)回滾時(shí)需要,在快照讀(select瓷们,當(dāng)讀的過程中有寫的事務(wù)開始和提交业栅,會(huì)造成讀數(shù)據(jù)的臟讀秒咐、不可重復(fù)讀、幻讀等)時(shí)也需要碘裕;所以不能隨便刪除携取,只有在快速讀或事務(wù)回滾不涉及該日志時(shí),對(duì)應(yīng)的日志才會(huì)被purge線程統(tǒng)一清除帮孔。


    purge:


    ????從前面的分析可以看出雷滋,為了實(shí)現(xiàn)InnoDB的MVCC機(jī)制,更新或者刪除操作都只是設(shè)置一下老記錄的deleted_bit文兢,并不真正將過時(shí)的記錄刪除晤斩。????


    ???為了節(jié)省磁盤空間,InnoDB有專門的purge線程來清理deleted_bit為true的記錄姆坚。為了不影響MVCC的正常工作澳泵,purge線程自己也維護(hù)了一個(gè)read view(這個(gè)read view相當(dāng)于系統(tǒng)中最老活躍事務(wù)的read view);


    ????如果某個(gè)記錄的deleted_bit為true,并且DB_TRX_ID相對(duì)于purge線程的read view可見兼呵,那么這條記錄一定是可以被安全清除的兔辅。


    舉個(gè)例子說明下:

    1、?比如一個(gè)有個(gè)事務(wù)插入person表插入了一條新記錄击喂,記錄如下维苔,name為Jerry, age為24歲,隱式主鍵是1懂昂,事務(wù)ID和回滾指針介时,我們假設(shè)為NULL


    2、現(xiàn)在來了一個(gè)事務(wù)1對(duì)該記錄的name做出了修改忍法,改為Tom潮尝。

    ? ? 在事務(wù)1修改該行(記錄)數(shù)據(jù)時(shí),數(shù)據(jù)庫會(huì)先對(duì)該行加排他鎖饿序,然后把該行數(shù)據(jù)拷貝到undo log中勉失,作為舊記錄,既在undo log中有當(dāng)前行的拷貝副本原探;

    ????拷貝完畢后乱凿,修改該行name為Tom,并且修改隱藏字段的事務(wù)ID為當(dāng)前事務(wù)1的ID, 我們默認(rèn)從1開始咽弦,之后遞增徒蟆,回滾指針指向拷貝到undo log的副本記錄,既表示我的上一個(gè)版本就是它



    3型型、又來了個(gè)事務(wù)2修改person表的同一個(gè)記錄段审,將age修改為30歲?

    ????在事務(wù)2修改該行數(shù)據(jù)時(shí),數(shù)據(jù)庫也先為該行加鎖

    ????然后把該行數(shù)據(jù)拷貝到undo log中闹蒜,作為舊記錄寺枉,發(fā)現(xiàn)該行記錄已經(jīng)有undo log了抑淫,那么最新的舊數(shù)據(jù)作為鏈表的表頭,插在該行記錄的undo log最前面姥闪。

    ????修改該行age為30歲始苇,并且修改隱藏字段的事務(wù)ID為當(dāng)前事務(wù)2的ID, 那就是2,回滾指針指向剛剛拷貝到undo log的副本記錄

    ????事務(wù)提交筐喳,釋放鎖催式。



    四、ReadView

    ??? ReadView說白了就是一個(gè)數(shù)據(jù)結(jié)構(gòu)避归,在SQL開始的時(shí)候被創(chuàng)建荣月。是事務(wù)進(jìn)行快照讀(select * from)操作的時(shí)候生產(chǎn)的讀視圖(Read View),在該事務(wù)執(zhí)行的快照讀的那一刻梳毙,會(huì)生成事務(wù)系統(tǒng)當(dāng)前的一個(gè)快照喉童,記錄并維護(hù)系統(tǒng)當(dāng)前活躍事務(wù)(未提交事務(wù))的ID(當(dāng)每個(gè)事務(wù)開啟時(shí),都會(huì)被分配一個(gè)ID, 這個(gè)ID是遞增的顿天,所以最新的事務(wù),ID值越大)蔑担。


    ReadView{low_trx_id, up_trx_id, trx_ids}

      • low_trx_id表示該SQL啟動(dòng)時(shí)牌废,當(dāng)前事務(wù)鏈表中最大的事務(wù)id編號(hào),也就是最近創(chuàng)建的除自身以外最大事務(wù)編號(hào)啤握;

      • up_trx_id表示該SQL啟動(dòng)時(shí)鸟缕,當(dāng)前事務(wù)鏈表中最小的事務(wù)id編號(hào),也就是當(dāng)前系統(tǒng)中創(chuàng)建最早但還未提交的事務(wù)排抬;

      • trx_ids表示所有事務(wù)鏈表中事務(wù)的id集合懂从。

    上述3個(gè)成員組成了ReadView中的主要部分,簡(jiǎn)單圖示如下:

    ????據(jù)上圖所示蹲蒲,所有數(shù)據(jù)行上DATA_TRX_ID小于up_trx_id的記錄番甩,說明修改該行的事務(wù)在當(dāng)前事務(wù)開啟之前都已經(jīng)提交完成,所以對(duì)當(dāng)前事務(wù)來說届搁,都是可見的缘薛。而對(duì)于DATA_TRX_ID大于low_trx_id的記錄,說明修改該行記錄的事務(wù)在當(dāng)前事務(wù)之后卡睦,所以對(duì)于當(dāng)前事務(wù)來說是不可見的宴胧。

    ????注意,ReadView是與SQL綁定的表锻,而并不是事務(wù)恕齐,所以即使在同一個(gè)事務(wù)中,每次SQL啟動(dòng)時(shí)構(gòu)造的ReadView的up_trx_id和low_trx_id也都是不一樣的瞬逊,至于DATA_TRX_ID大于low_trx_id本身出現(xiàn)也只有當(dāng)多個(gè)SQL并發(fā)的時(shí)候显歧,在一個(gè)SQL構(gòu)造完ReadView之后仪或,另外一個(gè)SQL修改了數(shù)據(jù)后又進(jìn)行了提交,對(duì)于這種情況追迟,數(shù)據(jù)其實(shí)是不可見的溶其。

    ????最后,至于位于(up_trx_id, low_trx_id)中間的事務(wù)是否可見敦间,這個(gè)需要根據(jù)不同的事務(wù)隔離級(jí)別來確定瓶逃。對(duì)于RC的事務(wù)隔離級(jí)別來說,對(duì)于事務(wù)執(zhí)行過程中廓块,已經(jīng)提交的事務(wù)的數(shù)據(jù)厢绝,對(duì)當(dāng)前事務(wù)是可見的,也就是說上述圖中带猴,當(dāng)前事務(wù)運(yùn)行過程中昔汉,trx1~4中任意一個(gè)事務(wù)提交,對(duì)當(dāng)前事務(wù)來說都是可見的拴清;而對(duì)于RR隔離級(jí)別來說靶病,事務(wù)啟動(dòng)時(shí),已經(jīng)開始的事務(wù)鏈表中的事務(wù)的所有修改都是不可見的口予,所以在RR級(jí)別下娄周,low_trx_id基本保持與up_trx_id相同的值即可。

    最后借用一種圖來解釋MySQL中實(shí)現(xiàn)的MVCC沪停。

    注:第四節(jié)插圖來源網(wǎng)易數(shù)據(jù)庫和大數(shù)據(jù)資深專家蔣鴻翔分享的文章中插圖煤辨。原發(fā)表于其個(gè)人博客?。



    …………………………………分割線……………………………

    不積跬步木张,無以至千里众辨;不積小流,無以成江海舷礼。

    關(guān)注我鹃彻,每天分享一些小知識(shí)點(diǎn)。分享自己的小心得妻献,包含但不限于初浮声、中、高級(jí)面試題呦P荨S净印!


    我都墨跡這么半天了 至朗,你不點(diǎn)關(guān)注屉符,不點(diǎn)贊,不收藏,還不轉(zhuǎn)發(fā)矗钟,你想干啥K粝恪!6滞А躬它!


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市东涡,隨后出現(xiàn)的幾起案子冯吓,更是在濱河造成了極大的恐慌,老刑警劉巖疮跑,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件组贺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡祖娘,警方通過查閱死者的電腦和手機(jī)失尖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渐苏,“玉大人掀潮,你說我怎么就攤上這事∏砀唬” “怎么了胧辽?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)公黑。 經(jīng)常有香客問我,道長(zhǎng)摄咆,這世上最難降的妖魔是什么凡蚜? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮吭从,結(jié)果婚禮上朝蜘,老公的妹妹穿的比我還像新娘。我一直安慰自己涩金,他們只是感情好谱醇,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著步做,像睡著了一般副渴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上全度,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天煮剧,我揣著相機(jī)與錄音,去河邊找鬼。 笑死勉盅,一個(gè)胖子當(dāng)著我的面吹牛佑颇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播草娜,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼挑胸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了宰闰?” 一聲冷哼從身側(cè)響起茬贵,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎议蟆,沒想到半個(gè)月后闷沥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咐容,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年舆逃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戳粒。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡路狮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蔚约,到底是詐尸還是另有隱情奄妨,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布苹祟,位于F島的核電站砸抛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏树枫。R本人自食惡果不足惜直焙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砂轻。 院中可真熱鬧奔誓,春花似錦、人聲如沸搔涝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庄呈。三九已至蜕煌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诬留,已是汗流浹背幌绍。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工颁褂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傀广。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓颁独,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親伪冰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子誓酒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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