mvcc原理

MVCC

mvcc

指的是一種提高并發(fā)的技術(shù)檩赢。最早的數(shù)據(jù)庫系統(tǒng),只有讀讀之間可以并發(fā)违寞,讀寫贞瞒,寫讀,寫寫都要阻塞趁曼。引入多版本之后军浆,只有寫寫之間相互阻塞,其他三種操作都可以并行挡闰,這樣大幅度提高了InnoDB的并發(fā)度乒融。在內(nèi)部實現(xiàn)中,與Postgres在數(shù)據(jù)行上實現(xiàn)多版本不同尿这,InnoDB是在undolog中實現(xiàn)的簇抵,通過undolog可以找回數(shù)據(jù)的歷史版本。找回的數(shù)據(jù)歷史版本可以提供給用戶讀(按照隔離級別的定義射众,有些讀請求只能看到比較老的數(shù)據(jù)版本)碟摆,也可以在回滾的時候覆蓋數(shù)據(jù)頁上的數(shù)據(jù)。在InnoDB內(nèi)部中叨橱,會記錄一個全局的活躍讀寫事務(wù)數(shù)組典蜕,其主要用來判斷事務(wù)的可見性

MySQL的大多數(shù)事務(wù)型存儲引擎實現(xiàn)的其實都不是簡單的行級鎖。基于提升并發(fā)性能的考慮, 它們一般都同時實現(xiàn)了多版本并發(fā)控制(MVCC)罗洗。不僅是MySQL, 包括Oracle,PostgreSQL等其他數(shù)據(jù)庫系統(tǒng)也都實現(xiàn)了MVCC, 但各自的實現(xiàn)機(jī)制不盡相同, 因為MVCC沒有一個統(tǒng)一的實現(xiàn)標(biāo)準(zhǔn)愉舔。

可以認(rèn)為MVCC是行級鎖的一個變種, 但是它在很多情況下避免了加鎖操作, 因此開銷更低。雖然實現(xiàn)機(jī)制有所不同, 但大都實現(xiàn)了非阻塞的讀操作伙菜,寫操作也只鎖定必要的行轩缤。

MVCC的實現(xiàn)方式有多種, 典型的有樂觀(optimistic)并發(fā)控制 和 悲觀(pessimistic)并發(fā)控制。

MVCC只在 READ COMMITTED 和 REPEATABLE READ 兩個隔離級別下工作贩绕。其他兩個隔離級別夠和MVCC不兼容, 因為 READ UNCOMMITTED 總是讀取最新的數(shù)據(jù)行, 而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行火的。而 SERIALIZABLE 則會對所有讀取的行都加鎖。

MVCC的實現(xiàn)依賴于:三個隱藏字段淑倾、Undo log和Read View

隱藏列

MySQL中會為每一行記錄生成隱藏列馏鹤,接下來就讓我們了解一下這幾個隱藏列吧。

(1)DB_TRX_ID:事務(wù)ID娇哆,是根據(jù)事務(wù)產(chǎn)生時間順序自動遞增的湃累,是獨一無二的勃救。如果某個事務(wù)執(zhí)行過程中對該記錄執(zhí)行了增、刪治力、改操作蒙秒,那么InnoDB存儲引擎就會記錄下該條事務(wù)的id。

(2)DB_ROLL_PTR:回滾指針宵统,本質(zhì)上就是一個指向記錄對應(yīng)的undo log的一個指針税肪,大小為 7 個字節(jié),InnoDB 便是通過這個指針找到之前版本的數(shù)據(jù)榜田。該行記錄上所有舊版本,在undo log中都通過鏈表的形式組織锻梳。

(3)DB_ROW_ID:行標(biāo)識(隱藏單調(diào)自增 ID)箭券,如果表沒有主鍵,InnoDB 會自動生成一個隱藏主鍵疑枯,大小為 6 字節(jié)辩块。如果數(shù)據(jù)表沒有設(shè)置主鍵,會以它產(chǎn)生聚簇索引荆永。

undo log

每當(dāng)我們要對一條記錄做改動時(這里的改動可以指INSERT废亭、DELETE、UPDATE)具钥,都需要把回滾時所需的東西記錄下來, 比如:

Insert undo log :插入一條記錄時豆村,至少要把這條記錄的主鍵值記下來,之后回滾的時候只需要把這個主鍵值對應(yīng)的記錄刪掉就好了骂删。

Delete undo log:刪除一條記錄時掌动,至少要把這條記錄中的內(nèi)容都記下來,這樣之后回滾時再把由這些內(nèi)容組成的記錄插入到表中就好了宁玫。

Update undo log:修改一條記錄時粗恢,至少要把修改這條記錄前的舊值都記錄下來,這樣之后回滾時再把這條記錄更新為舊值就好了欧瘪。InnoDB把這些為了回滾而記錄的這些東西稱之為undo log眷射。這里需要注意的一點是,由于查詢操作(SELECT)并不會修改任何用戶記錄佛掖,所以在查詢操作執(zhí)行時妖碉,并不需要記錄相應(yīng)的undo log。

每次對記錄進(jìn)行改動都會記錄一條undo日志苦囱,每條undo日志也都有一個DB_ROLL_PTR屬性嗅绸,可以將這些undo日志都連起來,串成一個鏈表撕彤,形成版本鏈鱼鸠。版本鏈的頭節(jié)點就是當(dāng)前記錄最新的值猛拴。

Read View

在可重復(fù)讀隔離級別下,我們可以把每一次普通的select查詢(不加for update語句)當(dāng)作一次快照讀蚀狰,而快照便是進(jìn)行select的那一刻愉昆,生成的當(dāng)前數(shù)據(jù)庫系統(tǒng)中所有未提交的事務(wù)id數(shù)組(數(shù)組里最小的id為min_id)和已經(jīng)創(chuàng)建的最大事務(wù)id(max_id)的集合,即我們所說的一致性視圖readview麻蹋。在進(jìn)行快照讀的過程中要根據(jù)一定的規(guī)則將版本鏈中每個版本的事務(wù)id與readview進(jìn)行匹配查詢我們需要的結(jié)果跛溉。

快照讀是不會看到別的事務(wù)插入的數(shù)據(jù)的。因此扮授,幻讀在“當(dāng)前讀”下才會出現(xiàn)芳室。快照讀的實現(xiàn)是基于多版本并發(fā)控制刹勃,即MVCC堪侯,可以認(rèn)為MVCC是行鎖的一個變種,但它在很多情況下荔仁,避免了加鎖操作伍宦,降低了開銷;既然是基于多版本乏梁,即快照讀可能讀到的并不一定是數(shù)據(jù)的最新版本次洼,而有可能是之前的歷史版本。MVCC只在 READ COMMITTED 和 REPEATABLE READ兩個隔離級別下工作遇骑,其他兩個隔離級別不和MVCC不兼容卖毁。因為READ UNCOMMITTED總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行质蕉,而SERIALIZABLE 則會對所有讀取的行都加鎖势篡。事務(wù)的快照時間點(即下文中說到的Read View的生成時間)是以第一個select來確認(rèn)的。所以即便事務(wù)先開始模暗,但是select在后面的事務(wù)的update之類的語句后進(jìn)行禁悠,那么它是可以獲取前面的事務(wù)的對應(yīng)的數(shù)據(jù)。

對于使用RC和RR隔離級別的事務(wù)來說兑宇,都必須保證讀到已經(jīng)提交了的事務(wù)修改過的記錄碍侦,也就是說假如另一個事務(wù)已經(jīng)修改了記錄但是尚未提交,是不能直接讀取最新版本的記錄的隶糕。核心問題就是:需要判斷一下版本鏈中的哪個版本是當(dāng)前事務(wù)可見的瓷产。為此,InnoDB提出了一個Read View的概念枚驻。

Read View就是事務(wù)進(jìn)行快照讀(普通select查詢)操作的時候生產(chǎn)的一致性讀視圖濒旦,在該事務(wù)執(zhí)行的快照讀的那一刻,會生成數(shù)據(jù)庫系統(tǒng)當(dāng)前的一個快照再登,它由執(zhí)行查詢時所有未提交的事務(wù)id數(shù)組(數(shù)組里最小的id為min_id)和已經(jīng)創(chuàng)建的最大事務(wù)id(max_id)組成尔邓,查詢的數(shù)據(jù)結(jié)果需要跟read view做對比從而得到快照結(jié)果晾剖。

快照規(guī)則

版本鏈比對規(guī)則:

如果落在綠色部分(trx_id<min_id),表示這個版本是已經(jīng)提交的事務(wù)生成的梯嗽,這個數(shù)據(jù)是可見的齿尽;

如果落在紅色部分(trx_id>max_id),表示這個版本是由將來啟動的事務(wù)生成的灯节,是肯定不可見的循头;

如果落在黃色部分(min_id<=trx_id<=max_id),那就包含兩種情況:

a.若row的trx_id在數(shù)組中炎疆,表示這個版本是由還沒提交的事務(wù)生成的卡骂,不可見;

b.若row的trx_id不在數(shù)組中形入,表示這個版本是已經(jīng)提交了的事務(wù)生成的偿警,可見。

演示:

當(dāng)我們執(zhí)行到第一個select的語句時唯笙,會生成readview[100,200],300,

[100,200] : 未提交事務(wù)數(shù)組

300:已創(chuàng)建的最大事務(wù)id

分析:

1.第一個查詢,當(dāng)前事務(wù)id:300 ,落在黃色區(qū)域盒使,是可見的崩掘,所以結(jié)果:馬云01

2.第二個查詢,當(dāng)前事務(wù)id:100 ,落在黃色區(qū)域少办,數(shù)據(jù)在未提交的數(shù)組中苞慢,所以不可見

就需要往前一個版本找,前一個版本事務(wù)Id:100,落在黃色區(qū)域英妓,所以也是不可見的挽放,

接著往前找,數(shù)據(jù)不在未提交的數(shù)組中蔓纠,所以可見辑畦,? 結(jié)果:馬云01

3.第三個查詢,因為是InnoDB的RR模式腿倚,所以readview不會更改纯出,仍為readview[100,200],300 當(dāng)前事務(wù)id:200,落在黃色區(qū)域,數(shù)據(jù)在未提交數(shù)組中敷燎,所以不可見暂筝,需要往前一個版本找,同第二個查詢硬贯,一直到找到事務(wù)id:300? ? 結(jié)果:馬云01

再來看一種情況:

如果重新開啟一個查詢

這個時候 事務(wù)100 300 都提交了焕襟,所以活躍事務(wù)數(shù)組中只有【200】? readView [200] 300

查詢時:事務(wù)id :200? 落在黃色區(qū)域,在數(shù)組中饭豹,不可見鸵赖,往前找 找到100 時务漩,在綠色區(qū)域 可見

所以結(jié)果? :馬云03

InnoDB下的RC模式,上文中提到的RC模式的數(shù)據(jù)讀都是讀最新的即當(dāng)前讀卫漫,所以readview是實時生成的

當(dāng)執(zhí)行到查詢的時候菲饼,readView[200] 300? 所以結(jié)果同樣為 馬云03

show global variables like '%isolation%';

set global transaction_isolation ='read-committed';

set global transaction_isolation ='repeatable-read';

Uodo log 什么時候刪除:

系統(tǒng)會判斷,沒有比這個undo log更早的read view的時候列赎,undo log會被刪除宏悦。所以這里也就是為什么我們建議你盡量不要使用長事務(wù)的原因。長事務(wù)意味著系統(tǒng)里面會存在很老的事務(wù)視圖包吝。由于這些事務(wù)隨時可能訪問數(shù)據(jù)庫里面的任何數(shù)據(jù)饼煞,所以這個事務(wù)提交之前,數(shù)據(jù)庫里面它可能用到的回滾記錄都必須保留诗越,這就會導(dǎo)致大量占用存儲空間

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砖瞧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嚷狞,更是在濱河造成了極大的恐慌块促,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件床未,死亡現(xiàn)場離奇詭異竭翠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)薇搁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門斋扰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人啃洋,你說我怎么就攤上這事传货。” “怎么了宏娄?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵问裕,是天一觀的道長。 經(jīng)常有香客問我孵坚,道長僻澎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任十饥,我火速辦了婚禮窟勃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逗堵。我一直安慰自己秉氧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布蜒秤。 她就那樣靜靜地躺著汁咏,像睡著了一般亚斋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上攘滩,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天帅刊,我揣著相機(jī)與錄音,去河邊找鬼漂问。 笑死赖瞒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚤假。 我是一名探鬼主播栏饮,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼磷仰!你這毒婦竟也來了袍嬉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤灶平,失蹤者是張志新(化名)和其女友劉穎伺通,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逢享,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡泵殴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拼苍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡调缨,死狀恐怖疮鲫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弦叶,我是刑警寧澤俊犯,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站伤哺,受9級特大地震影響燕侠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜立莉,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一绢彤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜓耻,春花似錦茫舶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讥耗。三九已至,卻和暖如春疹启,著一層夾襖步出監(jiān)牢的瞬間古程,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工喊崖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留挣磨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓贷祈,卻偏偏與公主長得像趋急,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子势誊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

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