PostgreSQL的MVCC vs InnoDB的MVCC

PostgreSQL的MVCC?vs InnoDB的MVCC

任何一個數(shù)據(jù)庫最主要功能之一是可擴(kuò)展挺勿。如果不刪除彼此饶米,則盡可能較少鎖競爭從而達(dá)到這個目的恒界。由于read睦刃、write、update十酣、delete是數(shù)據(jù)庫中最主要且頻繁進(jìn)行的操作涩拙,所以并發(fā)執(zhí)行這些操作時不被阻塞則顯得非常重要。為了達(dá)到這種目的耸采,大部分?jǐn)?shù)據(jù)庫使用多版本并發(fā)控制(Multi-Version Concurrency Control)這種并發(fā)模型兴泥。這種模型能夠?qū)⒏偁帨p少到最低限度。

MVCC是什么

Multi Version Concurrency Control ( MVCC)是這樣的一種算法:通過對同一個對象維護(hù)多個版本洋幻,提供一種很好的并發(fā)控制技術(shù)郁轻,這種技術(shù)能夠使READ和WRITE操作不發(fā)生沖突翅娶。這里的WRITE指的是UPDATE和DELETE文留,不包含Insert是因為新插入的記錄可以通過各自的隔離級別進(jìn)行保護(hù)好唯。每個WRITE操作使對象產(chǎn)生一個新版本,每個并發(fā)讀操作依賴于隔離級別讀取對象不同的版本燥翅。由于READ和WRITE操作同一個對象的不同版本骑篙,所以這些操作不需要將對象完全鎖住,因此這些操作能夠并發(fā)執(zhí)行森书。當(dāng)然當(dāng)兩個并發(fā)事務(wù)WRITE同一個記錄時靶端,這些鎖競爭還是會存在的。

當(dāng)前大部分?jǐn)?shù)據(jù)庫系統(tǒng)都支持MVCC凛膏。這個算法的核心是對相同對象維護(hù)不同版本杨名,因此不同數(shù)據(jù)庫創(chuàng)建并維護(hù)多版本的方式不同,其實現(xiàn)方式也不同猖毫。相應(yīng)地台谍,數(shù)據(jù)庫操作和數(shù)據(jù)存儲也發(fā)生變化。

實現(xiàn)MVCC最常見的方法:PostgreSQL使用的方法吁断、InnoDB和Oracle的使用方法趁蕊。下面我們會詳細(xì)討論PG和InnoDB的實現(xiàn)方式。

PostgreSQL中的MVCC

為了支持多版本仔役,PG對每個對象(PG術(shù)語:Tuple)增加了額外的字段:

1掷伙、xmin:進(jìn)行插入或更新操作事務(wù)的事務(wù)ID。UPDATE中又兵,對tuple的新版本分配該事務(wù)ID任柜。

2、xmax:進(jìn)行刪除或更新操作事務(wù)的事務(wù)ID寒波。UPDATE中乘盼,對當(dāng)前存在的tuple分配該事務(wù)ID。新創(chuàng)建的tuple俄烁,該字段默認(rèn)為null绸栅。

PostgreSQL將所有數(shù)據(jù)存儲在HEAP中(每頁默認(rèn)8KB)。新記錄的xmin為創(chuàng)建該記錄的事務(wù)的事務(wù)ID页屠;老版本(進(jìn)行update或delete)其xmax為進(jìn)行操作的事務(wù)的ID粹胯。會有一個鏈表將老版本和新版本連接起來。在回滾的過程中辰企,老版本記錄可以被重用风纠;依賴于隔離級別,READ語句讀取一個老版本記錄進(jìn)行返回牢贸。

例如下面兩條記錄:T1(值為1)竹观、T2(值為2),通過下面3步對記錄的創(chuàng)建進(jìn)行演示:

從圖中可以看出,數(shù)據(jù)庫中初始時存在兩個記錄:1和2臭增。

第二步懂酱,將2更新為3。此時創(chuàng)建一個新值誊抛,并存放到同一個存儲區(qū)域的下一個位置列牺。老版本2為其xmax分配該事務(wù)的ID,并且指向最新的版本記錄拗窃。

同理瞎领,第三步,當(dāng)T1被刪除時随夸,對記錄進(jìn)行虛擬刪除(為其xmax分配當(dāng)前事務(wù)ID)九默,該操作不存在創(chuàng)建新記錄版本。

下面宾毒,通過實例講解每個操作如何創(chuàng)建多版本荤西,不用加鎖如何實現(xiàn)事務(wù)的隔離級別。下面例子中使用默認(rèn)隔離級別“READ COMMITTED”伍俘。

INSERT

每次insert一個記錄邪锌,都會新創(chuàng)建一個tuple并將其存儲到表文件的頁中。

可以看到:

1癌瘾、Session-A開啟一個事務(wù)觅丰,其事務(wù)ID為495

2、Session-B開啟一個事務(wù)妨退,其事務(wù)ID為496

3妇萄、Session-A插入一個tuple,存儲到HEAP

4咬荷、新tuple的xmin為495冠句,而xmax為null

5、由于Session-A的事務(wù)沒有提交幸乒,session-B看不到第3步插入的值

6懦底、Session-A提交

7、都可以看到新插入的tuple

UPDATE

PostgreSQL的UPDATE不是“IN-PLACE”更新罕扎,不會將現(xiàn)有對象更新替換為新值聚唐,而是新創(chuàng)建一個新對象。因此UPDATE涉及以下幾步:

1腔召、將當(dāng)前對象標(biāo)記為deleted

2杆查、插入對象的一個新版本

3、將對象的老版本指向新版本

因此臀蛛,即使許多記錄保持不變亲桦,HEAP也會占用空間崖蜜,就像新插入另一個記錄一樣。

如上所示:

1客峭、Session-A開啟一個事務(wù)纳猪,其事務(wù)ID為497

2、Session-B開啟一個事務(wù)桃笙,其事務(wù)ID為498

3、Session-A更新一個現(xiàn)有記錄

4沙绝、Session-A可以看到tuple的最新版本而Session-B看到另一個老版本搏明。Session-A看到新記錄的xmin為497,xmax為null;Session-B看到老版本xmin是495闪檬,xmax為497即Session-A的事務(wù)ID星著。這兩個tuple版本都存在HEAP中,如果空間允許甚至存在同一頁中粗悯。

5虚循、Session-A提交事務(wù),老版本消失

6样傍、現(xiàn)在所有會話都可以看到記錄的同一個版本横缔。

DELETE

DELETE操作和UPDATE類似,只是不會添加一個新版本衫哥。如UPDATE茎刚,只是將當(dāng)前對象標(biāo)記為已刪除。

1撤逢、Session-A開啟一個事務(wù)膛锭,事務(wù)ID為499

2、Session-B開啟一個事務(wù)蚊荣,事務(wù)ID為500

3初狰、Session-A刪除現(xiàn)有記錄

4、Session-A看不到當(dāng)前事務(wù)已刪除的記錄互例;Session-B看到老版本奢入,其xmax為499,499的事務(wù)刪除的該記錄

5媳叨、Session-A提交事務(wù)俊马,老版本記錄消失

6、所有會話都看不到之前的老版本

可以看到肩杈,這些操作都不會直接刪除現(xiàn)有記錄柴我,如果需要會添加一個附加版本。

我們來看看SELECT在多版本中怎么執(zhí)行:依賴于隔離級別扩然,SELECT需要讀取tuple的所有版本直到找到合適的tuple艘儒。假設(shè)有一個tuple T1,被更新為新版本T1’,然后再被更新為T1’’:

1界睁、SELECT操作進(jìn)入這個表的heap中觉增,首先檢查T1,如果T1的xmax事務(wù)已提交翻斟,查找該tuple的下一個版本

2逾礁、T1’也被提交,查找下一個版本

3访惜、]最后找到T1’’看到xmax未提交或者為null嘹履,然后T1’’的xmin可見,最后讀取T1’’這個tuple债热。

可以看到需要遍歷該tuple的3個版本才能找到合適的可見版本砾嫉,直到VACUUM進(jìn)程回收了打上delete標(biāo)簽的記錄。

InnoDB中的MVCC

為了支持多版本窒篱,InnoDB對行記錄又額外維護(hù)了幾個字段:

1焕刮、DB_TRX_ID:插入或更新航記錄的事務(wù)的事務(wù)ID

2、DB_ROLL_PTR:即回滾指針墙杯,指向回滾段中的undo log record

與PostgreSQL相比庸诱,InnoDB也會創(chuàng)建行記錄的多版本疆虚,但是存儲老版本的方式不同。

InnoDB將行記錄的老版本存放到獨立的表空間/存儲空間(回滾段)。和PostgreSQL不同社痛,InnoDB僅將行記錄最新版本存儲到表的表空間中萧芙,而將老版本存放到回滾段下翎⊥缫保回滾段中的undo log作用:用來進(jìn)行回滾操作;依賴于隔離級別岩喷,進(jìn)行多版本讀恕沫,讀取老版本。

例如纱意,兩行記錄:T1(值為1)婶溯,T2(值為2),可以通過下面3步說明新記錄的創(chuàng)建過程:

從上圖可以看到偷霉,初始時迄委,表中有兩條記錄1和2。

第二階段类少,行記錄T2值2被更新為3叙身。此時記錄創(chuàng)建一個新版本并替代老版本。老版本存儲到回滾段(注意硫狞,回滾段中的數(shù)據(jù)僅包含更改值信轿,即delta value)晃痴,同時新版本行記錄中的回滾指針指向回滾段中的老版本。和PostgreSQL不同财忽,InnoDB更新是“IN-PLACE”倘核。

同理,第三步即彪,刪除T1然后將其標(biāo)記為虛擬刪除(僅在行記錄指定的一個bit位上打上delete標(biāo)簽)并在回滾段中插入一個對應(yīng)的新版本紧唱。同樣回滾指針指向回滾段中undo log。

從表面上看隶校,所有操作表象與PostgreSQL相同漏益,只是多版本在內(nèi)部存儲方式不同。

MVCC:PostgreSQL vs InnoDB

下面分析PostgreSQL和InnoDB的MVCC主要不同在哪幾方面:

1惠况、老版本的大小

PostgreSQL僅更新tuple老版本的xmax,因此老版本的大小和相應(yīng)插入的記錄大小相同宁仔。這意味著稠屠,如果一個older tuple有3個版本,那么他們大小都相同(如果更新的值大小不同翎苫,每次更新時實際大小就不同)权埠。

InnoDB的老版本存儲到回滾段,且比對應(yīng)的插入記錄小煎谍,因為InnoDB僅將變化的值寫到undo log攘蔽。

2、INSERT操作

INSERT時呐粘,InnoDB會向回滾段寫入額外的記錄满俗,而PostgreSQL僅在UPDATE中創(chuàng)建新版本。

3作岖、回滾時恢復(fù)老版本

回滾時唆垃,PostgreSQL不用任何特定內(nèi)容,需注意老版本的xmax等于update該記錄的事務(wù)ID痘儡。因此在并發(fā)快照中該記錄認(rèn)為是alive的直到該事務(wù)ID的事務(wù)提交辕万。

而InnoDB,一旦回滾沉删,需要重新構(gòu)造對象的老版本渐尿。

4、]回收老版本占用的空間

PG中矾瑰,老版本占用的空間僅在沒有并發(fā)快照使用時才可以被回收砖茸,此時被認(rèn)為dead。然后VACCUM可以回收空間殴穴。VACCUM可以手動觸發(fā)也可以依賴于配置在后臺任務(wù)中觸發(fā)渔彰。

InnoDB的undo log分為INSERT UNDO和UPDATE UNDO嵌屎。事務(wù)提交后,就會立即釋放INSERT UNDO恍涂。當(dāng)沒有其他并發(fā)快照使用時宝惰,才可以釋放UPDATE UNDO。InnoDB沒有顯示VACUUM操作但是有類似的PURGE回收undo log再沧。

5尼夺、延遲vacuum的影響

如前所示,PostgreSQL延遲vacuum存在很大影響炒瘸。即使頻繁執(zhí)行delete淤堵,它將會引起表膨脹造成占用的存儲空間暴增。這還會造成到達(dá)一個點后顷扩,需要執(zhí)行一個高額代價的操作VACUUM FULL拐邪。

6、表膨脹時的順序掃描

即使所有記錄都是dead狀態(tài)隘截,PostgreSQL的順序掃描也會掃描對象所有的老版本扎阶,直到執(zhí)行vacuum將dead的記錄刪除。這是PG中常見且經(jīng)常討論的問題婶芭。主要PG將一個tuple的所有老版本都存儲到同一個存儲區(qū)域东臀。

而InnoDB,除非需要犀农,否則不需要讀取undo log惰赋。如果所有undo記錄都已失效,那么只需要讀取所有對象的最新版本既可呵哨。

7赁濒、索引

PostgreSQL獨立存儲索引,并將索引連接到HEAP中的真實數(shù)據(jù)孟害。因此即使沒有更改索引流部,有時也需要更新索引。隨后這個問題被HOT(Heap Only Tuple)解決纹坐,但是仍有限制枝冀,如果相同頁空間不足,則退回到正常UPDATE操作耘子。

InnoDB由于使用聚集索引果漾,不會有這樣的問題。

結(jié)論

PostgreSQL的MVCC有一些缺點谷誓,尤其是具有頻繁UPDATE/DELETE負(fù)載時绒障,會引起表膨脹。因此決定選擇PG時捍歪,需要慎重配置VACUUM户辱。

PG社區(qū)已經(jīng)意識到這個問題鸵钝,已經(jīng)開始涉及基于undo的MVCC(暫命名為ZHEAP),我們在未來版本可以看到這個特性庐镐。

原文

https://severalnines.com/blog/comparing-data-stores-postgresql-mvcc-vs-innodb

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恩商,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子必逆,更是在濱河造成了極大的恐慌怠堪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件名眉,死亡現(xiàn)場離奇詭異粟矿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)损拢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門陌粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人福压,你說我怎么就攤上這事掏秩。” “怎么了隧膏?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵哗讥,是天一觀的道長嚷那。 經(jīng)常有香客問我胞枕,道長,這世上最難降的妖魔是什么魏宽? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任腐泻,我火速辦了婚禮,結(jié)果婚禮上队询,老公的妹妹穿的比我還像新娘派桩。我一直安慰自己,他們只是感情好蚌斩,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布铆惑。 她就那樣靜靜地躺著,像睡著了一般送膳。 火紅的嫁衣襯著肌膚如雪员魏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天叠聋,我揣著相機(jī)與錄音撕阎,去河邊找鬼。 笑死碌补,一個胖子當(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
  • 我被黑心中介騙來泰國打工葫隙, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躏仇。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓恋脚,卻偏偏與公主長得像腺办,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子糟描,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

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