基于MVCC,我用C++自己手?jǐn)]了個MySQL!
沒錯,真如標(biāo)題所示,我基于MVCC算法(這里我姑且叫它算法吧,畢竟在實(shí)際寫代碼時,確實(shí)是利用算法實(shí)現(xiàn)的),使用C++寫了個簡易版的MySQL,實(shí)現(xiàn)了簡易版的CRUD操作。
其實(shí),今天我并不打算先向小伙伴們演示我寫的簡易版MySQL,這個項(xiàng)目待我再優(yōu)化下,會開源出來的,到時大家可以一起學(xué)習(xí),一起進(jìn)步,一起來維護(hù)它。
今天,我想跟大家重點(diǎn)聊聊MVCC,網(wǎng)上關(guān)于MVCC的文章很多,大部分都是基于版本鏈進(jìn)行介紹的,其實(shí)對于初學(xué)者來說,使用版本鏈介紹MVCC其實(shí)還是挺難理解的。今天,我就來跟大家聊聊我是如何理解MVCC的,MVCC其實(shí)很簡單,不用版本鏈你也可以徹底理解透徹。
MVCC技術(shù)
MVCC是一種通過記錄數(shù)據(jù)的歷史版本來提升事務(wù)并發(fā)處理能力的一項(xiàng)技術(shù),它能夠極大的提升在并發(fā)事務(wù)下數(shù)據(jù)的處理性能,目前,大部分關(guān)系型數(shù)據(jù)庫都實(shí)現(xiàn)了MVCC機(jī)制。
MVCC主要解決多事務(wù)并發(fā)控制問題,也就是保證事務(wù)的隔離性。
MVCC的存儲方式
MVCC大體上可以分為三種存儲方式,分別為Append-Only方式、Delta方式和Time-Travle方式,如下所示。
(1)Append-Only方式:將數(shù)據(jù)的歷史版本直接存儲在數(shù)據(jù)表中,代表數(shù)據(jù)庫為PostgreSQL。
(2)Delta方式:將數(shù)據(jù)的增量歷史版本存儲在獨(dú)立的表空間,代表數(shù)據(jù)庫為MySQL和Oracle。
(3)Time-Travle方式:將數(shù)據(jù)的每個版本都全量存儲下來,代表數(shù)據(jù)庫為HANA。
MVCC的工作原理
MVCC主要用來保證事務(wù)的隔離性,這里,我們就分別以讀已提交和可重復(fù)讀兩種隔離級別為例,來聊聊MVCC是如何工作的。
讀已提交MVCC的工作原理
在讀已提交隔離級別下,當(dāng)前事務(wù)只能看到兩類數(shù)據(jù),如下所示。
(1)當(dāng)前事務(wù)自身產(chǎn)生的數(shù)據(jù)。
(2)當(dāng)前事務(wù)開啟之前,其他已經(jīng)提交的事務(wù)所產(chǎn)生的數(shù)據(jù)。
為了便于小伙伴們理解,這里我畫了一張簡易的事務(wù)執(zhí)行圖,如下所示。
事務(wù)A到事務(wù)E是在數(shù)據(jù)庫中執(zhí)行的五個事務(wù),它們按照先后順序執(zhí)行,分別操作的是數(shù)據(jù)表中data1~data5的五條記錄。在t1時刻,啟動事務(wù)E,事務(wù)E要讀取事務(wù)A到事務(wù)D的這四條記錄,在t1時刻,事務(wù)E啟動時,會向系統(tǒng)申請一個活動事務(wù)列表,所謂的活動事務(wù),就是已經(jīng)啟動但是并未提交或者回滾的事務(wù)。
所以,在申請的活動事務(wù)列表中會看到事務(wù)D,當(dāng)事務(wù)E查詢到data4這條數(shù)據(jù)記錄時,其對應(yīng)的事務(wù)D正好在活動事務(wù)列表中,事務(wù)E就會讀取data4的上一個版本。
而事務(wù)A、事務(wù)B和事務(wù)C在事務(wù)E啟動時已經(jīng)提交,并且最新版本的事務(wù)id小于活動事務(wù)D對應(yīng)的事務(wù)id,所以事務(wù)E能夠看到事務(wù)A、事務(wù)B和事務(wù)C對應(yīng)的data1、data2和data3記錄的最新版本。
可重復(fù)讀MVCC的工作原理
在重復(fù)讀隔離級別下,MVCC又是如何工作的呢?先來看張圖。
如果在讀已提交隔離級別下,則在t1時刻,事務(wù)E啟動時,事務(wù)A、事務(wù)B和事務(wù)C已經(jīng)提交,所以,事務(wù)E能夠讀取到事務(wù)A、事務(wù)B和事務(wù)C對應(yīng)的data1、data2和data3記錄的最新版本。而事務(wù)D屬于活動事務(wù),所以,事務(wù)E能夠讀取到data4的上一個版本。
事務(wù)E執(zhí)行到t2時刻時,事務(wù)D也已經(jīng)提交,按照之前的分析可知,在t2時刻,事務(wù)E能夠讀取到事務(wù)A、事務(wù)B、事務(wù)C和事務(wù)D對應(yīng)的數(shù)據(jù)data1、data2、data3和data4的最新版本。
在可重復(fù)讀隔離級別下,這顯然是不符合要求的。
在可重復(fù)讀隔離級別下,MVCC機(jī)制是如何解決這個問題的呢?
其實(shí)解決的辦法很簡單,就是在系統(tǒng)中記錄下t1時刻啟動事務(wù)E時的活動事務(wù)列表,在事務(wù)E執(zhí)行的過程中,一直使用在t1時刻記錄的活動事務(wù)列表即可,這個一直使用的活動事務(wù)列表被稱為“快照”。
很顯然,在t2時刻使用在t1時刻保存的活動事務(wù)列表,則事務(wù)E在t1時刻和t2時刻讀取到的數(shù)據(jù)是一致性。
讀已提交與可重復(fù)讀MVCC的區(qū)別
讀已提交隔離級別下每個SQL語句都會有一個自己的快照,它們看到的數(shù)據(jù)庫中的數(shù)據(jù)是不同的。而在可重復(fù)讀隔離級別下,所有的SQL語句使用同一個快照,能夠看到數(shù)據(jù)庫中同樣的數(shù)據(jù)。
快照優(yōu)化
在實(shí)現(xiàn)MVCC時,并只是簡單的存儲事務(wù)id列表,而是會統(tǒng)計最小活動事務(wù)id和最大已提交事務(wù)id,這樣做的好處是:大部分事務(wù)id通過比較這些邊界值就能夠迅速判別是讀取最新版本還是上一個版本,如果事務(wù)id正好落在這些邊界值的范圍之內(nèi),則只需要進(jìn)一步查找當(dāng)前事務(wù)id是否與活動事務(wù)的id相匹配即可。如果相匹配,則說明當(dāng)前事務(wù)是活動事務(wù),可以看到當(dāng)前數(shù)據(jù)。
好了,關(guān)于MVCC,小伙伴們,你們理解了嗎?理解透徹后,再學(xué)習(xí)下MySQL的底層原理,有條件的話,閱讀下MySQL的源碼,然后跟冰河一起手寫MySQL。