Mvcc 學(xué)習(xí)筆記

Leaving things better than you found them

MVCC 筆記


MVCC為了解決什么問題址貌?

多版本并發(fā)控制吼畏,針對在并發(fā)訪問數(shù)據(jù)庫時對于數(shù)據(jù)版本的控制以及隔離性問題龙亲,Mysql使用了MVCC的思路來進(jìn)行版本控制

MVCC的MYSQL 實(shí)現(xiàn)淺析篮条?

Mysql 的 MVCC實(shí)現(xiàn)大致是通過隱藏列中的DB_ROLL_PTR字段以及undo log的方式生成數(shù)據(jù)版本鏈凉逛,在創(chuàng)建事務(wù)時生成ReadView來進(jìn)行版本比對馍驯,從而篩選出當(dāng)前事務(wù)可見的數(shù)據(jù)行

事務(wù)并發(fā)執(zhí)行會遇到的問題阁危?

臟寫(Dirty Write):一個事務(wù)修改了另一個事務(wù)未提交過的數(shù)據(jù)

臟讀(Dirty Read):一個事務(wù)讀取到了另一個事務(wù)未提交過的數(shù)據(jù)

不可重復(fù)讀(Non-Repeatable Read):一個事務(wù)只能讀取到另一個已經(jīng)提交的事務(wù)修改過的數(shù)據(jù),并且其他事務(wù)每對該數(shù)據(jù)進(jìn)行一次修改并提交后汰瘫,該事物都能查到最新的值(在一個事務(wù)中的多次查詢狂打,可以查詢到多個其他事務(wù)提交的最新值)

幻讀(Phantom):一個事務(wù)根據(jù)某些條件查詢出一些記錄之后,另一個事務(wù)又向表中插入了符合這些條件的記錄混弥,原先的事務(wù)用相同的條件再次查詢時趴乡,能把另外一個事務(wù)插入的數(shù)據(jù)也查詢出來

按問題嚴(yán)重性排序:

臟寫 > 臟讀 > 不可重復(fù)讀 > 幻讀

標(biāo)準(zhǔn)的四種 SQL事務(wù)隔離級別(并非Mysql定義)

Read UnCommittd:未提交讀

Read Committd:已提交讀

Repeatable Read:可重復(fù)讀

Serializable:可串行化

隔離級別 臟讀 不可重復(fù)讀 幻讀
Read UnCommittd Possible Possible Possible
Read Committd Not Possible Possible Possible
Repeatable Read Not Possible Not Possible Possible
serializable Not Possible Not Possible Not Possible

也就是說:

Read UnCommittd 隔離級別下,可能發(fā)生臟讀蝗拿、不可重復(fù)讀幻讀問題

Read Committd 隔離級別下晾捏,可能發(fā)生不可重復(fù)讀幻讀問題

Repeatable Read 隔離級別下,可能會發(fā)生幻讀但是在Mysql中哀托,Repeatable Read隔離級別可以處理幻讀的問題

serializable 隔離級別下惦辛,各種問題都不會發(fā)生

至于臟寫,應(yīng)為臟寫實(shí)在太嚴(yán)重了仓手,所以無論哪個隔離級別都不允許臟寫的情況發(fā)生胖齐。

什么是版本鏈 ? undo日志 嗽冒?

undo日志:用于記錄事務(wù)中未提交的變更記錄呀伙,主要用于保證事務(wù)的原子性添坊,任何對數(shù)據(jù)的操作都會記錄到undo日志中剿另,直到提交事務(wù)或者rollback,才會進(jìn)行清理谚攒。

說起版本鏈,我們得先有行格式的概念,我們大概看一下Compact格式下所看到的一行數(shù)據(jù)的格式:

行格式.png

在一個正常的行信息中收津,除了記錄了用戶的真實(shí)記錄以外,innoDB還會為每條記錄都添加2個隱藏列以及一個可選列

列名 是否必須 占用空間 描述
DB_TRX_ID 6字節(jié) 事務(wù)ID
DB_ROLL_PTR 7字節(jié) 回滾指針
DB_ROW_ID 6字節(jié) 行id赵抢,唯一標(biāo)識一條記錄

DB_ROW_ID 在沒有自定義主鍵以及存在非Null的Unique鍵時才會添加該列

這里來簡單闡述一下DB_TRX_ID以及DB_ROLL_PTR在版本鏈中的作用

DB_TRX_ID:每次一個事務(wù)對某條聚簇索引記錄進(jìn)行改動時伸蚯,都會把該事務(wù)的事務(wù)id賦值給該記錄的DB_TRX_ID隱藏列憨栽,注意事務(wù)id是遞增的旁涤。

DB_ROLL_PTR:每次對某條聚簇索引記錄改動時,都會將舊的版本寫入到 undo日志中舔箭,然后然后這個隱藏列就相當(dāng)于一個指針箫章,可以通過它來找到該記錄修改前的信息烙荷。

注意insert是不會產(chǎn)生DB_ROLL_PRT的,因?yàn)閕nsert時并沒有更早的版本存在

了解到這里我們大概就能看到版本鏈的雛形了檬寂,也就是利用了DB_ROLL_PTR來鏈接上一個版本的數(shù)據(jù)终抽;

我們以一個hero表為例:


hero table.png

假如我們有一個hero表其中number為1的記錄name初始化為劉備,我們執(zhí)行如下兩個語句:

hero 事務(wù).png

它的版本鏈大概就是下面這個樣子:


version list1.png

每次對該記錄更新后桶至,都會將舊值放到 undo 日志中昼伴,隨著更新次數(shù)的增多,所有版本都會被DB_ROLL_PRT屬性鏈接成為一個鏈表塞茅,我們把這個鏈表稱之為版本鏈亩码,版本鏈的頭節(jié)點(diǎn)就是當(dāng)前記錄最新的值季率,另外野瘦,每個版本中還包含生成該版本時對應(yīng)的事務(wù)id

ReadView 是什么飒泻?

ReadView 可以按字面意思理解為讀視圖鞭光,也就是在事務(wù)開始時生成的一個快照,ReadView的設(shè)計(jì)主要是為了解決 "判斷版本鏈中哪個版本是當(dāng)前事務(wù)可見" 的問題

SERIALIZABLE隔離級別采用加鎖的方式來訪問記錄泞遗,而READ COMMITTEDREPEATABLE READ隔離級別在事務(wù)的不同階段會創(chuàng)建ReadView

Read committed 隔離級別下惰许,每次讀取數(shù)據(jù)前都會生產(chǎn)一個ReadView

Repeatable Read 隔離級別下,在第一次讀取數(shù)據(jù)時生產(chǎn)一個ReadView

關(guān)于兩種隔離級別下產(chǎn)生ReadView時機(jī)不同帶來的影響史辙,后面描述

ReadView 主要組成結(jié)構(gòu):

m_ids:表示在生成ReadView時當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)的事務(wù)id列表

min_trx_id:表示在生成ReadView時當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)中最小的事務(wù)id汹买,也就是m_ids中最小的值

max_trx_id:表示生成ReadView時系統(tǒng)中應(yīng)該分配給下一個事務(wù)的事務(wù)id

  max_trx_id并非是是m_ids中的最大值,事務(wù)id是遞增分配的聊倔,比方說現(xiàn)在有id為1晦毙,2,3這三個事務(wù)耙蔑,之后id為3的事務(wù)提交了见妒,那么一個新的讀事務(wù)在生產(chǎn)ReadView時,m_ids時就包括1和2甸陌,min_trx_id的值就是1须揣,max_trx_id的值就是4

creator_trx_id:表示生成該ReadView的事務(wù)的事務(wù)id

  只有在對表中的記錄做改動時(執(zhí)行Insert、update钱豁、delete)才會為事務(wù)分配事務(wù)id耻卡,否則在一個只讀事務(wù)中,事務(wù)id都默認(rèn)為0

當(dāng)生成了這個ReadView牲尺,這樣在訪問某條記錄時卵酪,只需按照下邊的步驟判斷記錄的某個版本是否可見(可見性要求):

  • 如果被訪問版本的 trx_id 屬性值與 ReadView 中的 creator_trx_id 值相同,意味著當(dāng)前事務(wù)在訪問他自己修改過的記錄,所以該版本可以被當(dāng)前事務(wù)訪問

  • 如果被訪問版本的trx_id屬性值小于ReadView中的min_trx_id值凛澎,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView前已經(jīng)提交霹肝,所以該版本可以被當(dāng)前事務(wù)訪問

  • 如果被訪問版本的trx_id屬性值大于ReadView中的max_trx_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView后才開啟塑煎,所以該版本不可以被當(dāng)前事務(wù)訪問

  • 如果被訪問版本的trx_id屬性值在ReadViewmin_trx_idmax_trx_id之間沫换,那就需要判斷一下trx_id屬性是不是在m_ids列表中,如果在最铁,說明創(chuàng)建ReadView時生成該版本的事務(wù)還是活躍的讯赏,該版本不可以被訪問,如果不在冷尉,說明創(chuàng)建ReadView時生成該版本的事務(wù)已經(jīng)被提交漱挎,該版本可以被訪問

Read Committd 每次讀取數(shù)據(jù)前都生成一個ReadView

假如現(xiàn)在系統(tǒng)中有兩個事務(wù)在執(zhí)行,事務(wù)id分別是100雀哨、200

trx 2.png

版本鏈如下:


version list2.png

假設(shè)現(xiàn)在有一個使用Read Committd隔離級別的事務(wù)開始執(zhí)行:

trx read committed.png

那么這個Select1的執(zhí)行過程如下:

  1. 在執(zhí)行SELECT 語句時會現(xiàn)生成一個ReadView磕谅,ReadViewm_ids列表內(nèi)容為[100,200],min_trx_id為100,max_trx_id為201雾棺,creator_trx_id為0

  2. 然后從版本鏈中挑選可見記錄膊夹,從圖中可以看出,最新版本的列name的內(nèi)容是 '張飛'捌浩,該版本的事務(wù)id為100放刨,在m_ids內(nèi),所以不符合可見性要求尸饺,根據(jù)DB_ROLL_PTR(roll_pointer)找到下一個版本

  3. 下一個版本的列name的值為 '關(guān)羽'进统,該數(shù)據(jù)的事務(wù)id也為100,在m_ids范圍內(nèi)浪听,不符合可見性要求螟碎,繼續(xù)跳到下一個版本

  4. 下一個版本的列name的值為 '劉備',該版本的事務(wù)id為80馋辈,小于ReadView中的min_trx_id值100抚芦,所以這個版本符合可見性要求,最終返回給用戶的就是這條name列為 '劉備' 的數(shù)據(jù)

之后迈螟,我們把事務(wù)id100的事務(wù)提交一下:

commit transaction.png

然后再到事務(wù)id為200的事務(wù)中更新一下hero表中number為1的數(shù)據(jù):


update trx 200.png

此刻表hero中number為1的記錄的版本鏈如下:


version list3.png

然后我們再到剛才使用Read Committd隔離級別的事務(wù)中繼續(xù)查找這個number為1的記錄叉抡,如下:

SELECT 2.png

其中的Select2的執(zhí)行過程如下:

  1. 在執(zhí)行SELECT2 語句時又會單獨(dú)生成一個ReadView,該ReadViewm_ids列表內(nèi)容是[200](事務(wù)id為100的那個事務(wù)已經(jīng)提交了答毫,所以再次生成快照時就沒有它了)褥民,min_trx_id為200,max_trx_id為201洗搂,creator_id為0

  2. 然后從版本鏈中挑選可見的記錄消返,從圖中可以看出载弄,最新版本的列name值為 '諸葛亮',該版本的事務(wù)id為200撵颊,在m_ids列表內(nèi)宇攻,所以不符合可見性要求,根據(jù)DB_ROLL_PTR(roll_pointer)跳到下一個版本倡勇。

  3. 下一個版本的列name的值為 '趙云'逞刷,該版本的事務(wù)id為200,在m_ids列表內(nèi)妻熊,所以也不符合要求夸浅,繼續(xù)跳到下一個版本

  4. 下一個版本的列name的值為 '張飛',該版本的事務(wù)id為100扔役,小于ReadViewmin_trx_id的值200帆喇,所以符合要求,最后返回給用戶的版本就是這條列name為 '張飛' 的記錄

可以看到在Read Committd的隔離級別下亿胸,出現(xiàn)了不可重復(fù)讀的場景

Read Committd隔離級別下坯钦,事務(wù)在每次查詢開始時都會創(chuàng)建一個獨(dú)立的ReadView,關(guān)于Repeatable Read隔離級別下版本鏈以及執(zhí)行過程大概類似這里就不闡述了(歡迎討論)损敷,只是在Repeatable Read隔離級別下葫笼,在事務(wù)中多次讀數(shù)據(jù)時深啤,只會在第一次讀取數(shù)據(jù)時創(chuàng)建ReadView拗馒,后面的查詢都會復(fù)用第一次創(chuàng)建的ReadView,這就保證了前后兩次查詢到的結(jié)果一致溯街,可以嘗試使用Repeatable Read隔離級別的特性去看看上面的版本鏈诱桂,select2在Repeatable Read級別下應(yīng)該返回什么?怎么去理解可重復(fù)度呈昔?


總結(jié):

從上邊的描述中我們可以看出來挥等,所謂的MVCC(Multi-Version Concurrency Control ,多版本并發(fā)控制)指的就是在使用Read Committd堤尾、Repeatable Read這兩種隔離級別的事務(wù)在執(zhí)行普通的SEELCT操作時訪問記錄的版本鏈的過程肝劲,這樣子可以使不同事務(wù)的讀-寫、寫-讀操作并發(fā)執(zhí)行郭宝,從而提升系統(tǒng)性能辞槐。Read CommittdRepeatable Read這兩個隔離級別的一個很大不同就是:生成ReadView的時機(jī)不同粘室,Read Committd在每一次進(jìn)行普通SELECT操作前都會生成一個ReadView榄檬,而Repeatable Read只在第一次進(jìn)行普通SELECT操作前生成一個ReadView,之后的查詢操作都重復(fù)使用這個ReadView就好了衔统。

疑問鹿榜?

undo log理論上會在事務(wù)提交后進(jìn)行刪除海雪,那么版本鏈如何形成呢?

實(shí)際 insert undo 在事務(wù)提交之后就可以被釋放了舱殿,update undo由于還需要支持MVCC奥裸,不能立即刪除掉,實(shí)際在行結(jié)構(gòu)中除了隱藏列還有一個delete mark的標(biāo)記位沪袭,1代表刪除刺彩,0代表未刪除,用來記錄數(shù)據(jù)是否被刪除枝恋,所以在上面的版本鏈判斷數(shù)據(jù)時并非是簡單的判斷事務(wù)id创倔,同時還會考慮這個delete_mark標(biāo)記,同時在mysql中焚碌,作者為了減少因?yàn)橐瞥龜?shù)據(jù)后的磁盤重新排列的性能問題畦攘,還搞了一個所謂的垃圾鏈表,在這個鏈表中的記錄占用的空間稱之為所謂的可重用空間十电,之后如果有新記錄要插入到表中的話知押,可能會把這些被刪除記錄占用的存儲空間給覆蓋掉,當(dāng)然也并非所有被標(biāo)記了刪除都數(shù)據(jù)都是覆蓋處理鹃骂,這里就涉及到mysql的后臺的purge線程的作用了台盯,后面再去了解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市畏线,隨后出現(xiàn)的幾起案子静盅,更是在濱河造成了極大的恐慌,老刑警劉巖寝殴,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒿叠,死亡現(xiàn)場離奇詭異,居然都是意外死亡蚣常,警方通過查閱死者的電腦和手機(jī)市咽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抵蚊,“玉大人施绎,你說我怎么就攤上這事≌晟” “怎么了谷醉?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熔酷。 經(jīng)常有香客問我孤紧,道長,這世上最難降的妖魔是什么拒秘? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任号显,我火速辦了婚禮臭猜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘押蚤。我一直安慰自己蔑歌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布揽碘。 她就那樣靜靜地躺著次屠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雳刺。 梳的紋絲不亂的頭發(fā)上劫灶,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機(jī)與錄音掖桦,去河邊找鬼本昏。 笑死,一個胖子當(dāng)著我的面吹牛枪汪,可吹牛的內(nèi)容都是我干的涌穆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼雀久,長吁一口氣:“原來是場噩夢啊……” “哼宿稀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赖捌,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤祝沸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后巡蘸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奋隶,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年悦荒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘹吨。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡搬味,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蟀拷,到底是詐尸還是另有隱情碰纬,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布问芬,位于F島的核電站悦析,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏此衅。R本人自食惡果不足惜强戴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一亭螟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骑歹,春花似錦预烙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至最域,卻和暖如春谴分,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镀脂。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工狸剃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狗热。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓钞馁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親匿刮。 傳聞我的和親對象是個殘疾皇子僧凰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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

  • 事務(wù)的四大特性(簡稱ACID) 數(shù)據(jù)庫如果支持事務(wù)的操作,那么就具備以下四個特性: 1熟丸、原子性(Atomicity...
    gregoriusxu閱讀 307評論 0 0
  • 事務(wù)就是要保證一組數(shù)據(jù)庫操作训措,要么全部成功,要么全部失敗光羞。在 MySQL 中绩鸣,事務(wù)支持是在引擎層實(shí)現(xiàn)的。MySQL...
    itczl閱讀 991評論 0 0
  • 【2018-03-03】開學(xué)同聽“第一課” 今天是我第一次和孩子做同學(xué)纱兑,同窗同聽呀闻,親子共修,感覺棒棒噠潜慎〖穸啵回眸一整天...
    林文斌閱讀 226評論 0 0
  • 百度“等價(jià)交換”:等價(jià)交換是不同效用的商品按照它們各自具有的價(jià)格相交換。是商品交換的一般原則铐炫,是不同使用價(jià)值...
    愛冰淇淋的兔子閱讀 1,061評論 0 1
  • 文/ 麥醺 大學(xué)畢業(yè)后倒信,我漂在西北的一個三線城市里科贬。當(dāng)時剛上班的我和同事合租在一個50平...
    麥小醺閱讀 770評論 2 3