什么是MVCC,一文搞懂MySQL的MVCC机制
MVCC是什么
MVCC,即Multi-Version Concurrency Control (多版本并發(fā)控制)。它是一種并發(fā)控制的方法,一般在數(shù)據(jù)庫管理系統(tǒng)中,實(shí)現(xiàn)對數(shù)據(jù)庫的并發(fā)訪問,在編程語言中實(shí)現(xiàn)事務(wù)內(nèi)存。
數(shù)據(jù)庫中同時存在多個版本的數(shù)據(jù),并不是整個數(shù)據(jù)庫的多個版本,而是某一條記錄的多個版本同時存在,在某個事務(wù)對其進(jìn)行操作的時候,需要查看這一條記錄的隱藏列事務(wù)版本id,比對事務(wù)id并根據(jù)事物隔離級別去判斷讀取哪個版本的數(shù)據(jù)。
數(shù)據(jù)庫隔離級別讀已提交、可重復(fù)讀 都是基于MVCC實(shí)現(xiàn)的,相對于加鎖簡單粗暴的方式,它用更好的方式去處理讀寫沖突,能有效提高數(shù)據(jù)庫并發(fā)性能。
我們先回顧一下事務(wù)的相關(guān)概念,以便更好的理解mvcc的實(shí)現(xiàn)原理
MySQL的事務(wù)
redo log
redo log重做日志,是記錄物理數(shù)據(jù)變化的日志,使用數(shù)據(jù)庫DML對數(shù)據(jù)的修改操作,都會產(chǎn)生redo log,可以保證事務(wù)的持久性
undo log
undo log是回滾日志,有兩個作用:提供回滾操作和多行版本控制(MVCC)
在數(shù)據(jù)修改的時候,不僅記錄了redo,還記錄了相對應(yīng)的undo,如果因?yàn)槟承┰驅(qū)е率聞?wù)失敗或回滾了,可以借助undo進(jìn)行回滾,
undo log和redo log記錄物理日志不一樣,undo log主要記錄的是數(shù)據(jù)的邏輯變化,它是邏輯日志
可以認(rèn)為執(zhí)行insert時會對應(yīng)在undo log中記錄一條delete語句,并且會記錄這個版本的事務(wù)id(txid),執(zhí)行update語句會有一條update語句來使之?dāng)?shù)據(jù)恢復(fù)到上個版本。
當(dāng)執(zhí)行rollback時,就可以從undo log中的邏輯記錄讀取到相應(yīng)的內(nèi)容并進(jìn)行回滾
多行版本控制的時候,也是通過undo log來實(shí)現(xiàn)的
當(dāng)讀取的某一行被其它事務(wù)鎖定時,它可以從undo log中分析出該行記錄以前的數(shù)據(jù)是什么。從而提供該行版本信息。
并且undo log的內(nèi)容變更也會記錄到redo log中,從而實(shí)現(xiàn)undo log的持久化
總而言之就是undo log提供了一種數(shù)據(jù)庫快照的功能,通過事務(wù)id和undo log我們可以找到歷史版本的數(shù)據(jù)
MySQL事務(wù)的隔離級別
SQL標(biāo)準(zhǔn)中定義了四個隔離級別:
- READ-UNCOMMITTED(讀取未提交):最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更,可能導(dǎo)致臟讀、幻讀、不可重復(fù)讀;
- READ-COMMITTED(讀取已提交):允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀任有可能發(fā)生
- REPAATABLE-READ(可重復(fù)讀):對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)本身是被本身事務(wù)所修改的,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生
- SETIALIZABLE(可串行化):最高的隔離級別,完全服從ACID原則,所有事務(wù)單個依次執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀,不可重復(fù)讀以及幻讀。
MVCC原理
事務(wù)版本號
事務(wù)每次開啟前,都會從數(shù)據(jù)庫獲取一個自增長的事務(wù)ID,可以從事務(wù)ID判斷事務(wù)的執(zhí)行先后順序
隱式字段
對于InnoDB存儲引擎,每一行記錄都有兩個隱藏列trx_id、roll_ptr,如果表中沒有主鍵和非NULL唯一鍵時,則還會有第三個隱藏的主鍵列row_id。
MySQL行記錄中除了記錄業(yè)務(wù)數(shù)據(jù)外,還有隱藏的 trx_id 和 roll_ptr
- trx_id:表示最近修改的事務(wù)的id,每次一個事務(wù)對某條聚集索引記錄進(jìn)行改動時,都會把該事務(wù)的事務(wù)id賦值給trx_id隱藏列,新增一個事務(wù)時,trx_id就會遞增,因此trx_id能夠表示事務(wù)開始的先后順序
- roll_ptr:指向上一個版本的地址,每次對某條聚集索引記錄進(jìn)行改動時,都會把舊版本寫入undo log中,然后這個隱藏列就相當(dāng)于一個指針,可以通過它來找到該記錄修改前的信息。
版本鏈
MySQL的每行記錄邏輯上其實(shí)是一個鏈表
演示說明:
update user set name = '肉蟹寶1' where id = 1這個鏈表存在于undo log中,和最新版本的數(shù)據(jù)不在一起
每次更新后,都會將舊值放在一條undo log中,就算是該記錄的一個舊版本,隨著更新次數(shù)的增多,所有的版本都會被roll_ptr屬性連接成一個鏈表,我們把這個鏈表稱為版本鏈,版本鏈的頭節(jié)點(diǎn)就是當(dāng)前記錄最新的值
另外,每個版本中還包含生成該版本時對應(yīng)的事務(wù)id(trx_id)
ReadView
說完了undo log 和版本鏈,再來說說ReadView,前面我們說過MVCC只在read_commited和repeatable_read兩個隔離級別下工作,而read_commited和repeatable_read的區(qū)別不同就在于它們生成的ReadView的策略不同
對于使用read_commited和repeatable_read隔離級別的事務(wù)來說,都必須保證讀到已經(jīng)提交了的事務(wù)修改過的記錄,也就是說假如另一個事務(wù)已經(jīng)修改了記錄但未提交,是不能直接讀取最新版本的記錄的
核心問題就是:需要判斷一下,版本鏈中哪個版本是當(dāng)前事務(wù)可見的
為此InnoDB提出了一個ReadView的概念,這個ReadView中有一個id列表,trx_ids來存儲系統(tǒng)中當(dāng)前活躍著的讀寫事務(wù),也就是begin了但是還未commit或rollback的事務(wù)
流程說明:
演示說明:
提交了trx_id是2的記錄后,接著新建trx_id為3的事務(wù),修改name的值,但是事務(wù)還沒有提交
update user set name = '肉蟹寶 2 ' where id = 1則此時的版本鏈?zhǔn)?#xff1a;
顯然,此時的trx_ids為[3]
如果另一個事務(wù)查詢id為1的記錄,因?yàn)閠rx_ids當(dāng)前只有trx_id為3的事務(wù),而trx_ids的意義是記錄未完成的事務(wù)。在這里,事務(wù)未完成,所以該條記錄不可見,繼續(xù)查詢下一條,結(jié)果返回肉蟹寶1
此時我把trx_id為3的事務(wù)提交了,并且新建了一個trx_id為4的事務(wù),修改name,且不提交事務(wù)
update user set name = '肉蟹寶 3 ' where id = 1這時的版本鏈就是:
read-committed —— 每次查詢數(shù)據(jù)前都生成一個 ReadView
trx_ids 將更新為[ 4 ],版本鏈通過 trx_id 對比查找到的結(jié)果就是肉蟹寶2。
repeatable-read —— 在第一次查詢數(shù)據(jù)時生成一個 ReadView,之后的讀都復(fù)用之前的。
不會有重建的 ReadView , trx_ids 還是 [ 3 ],MySQL 認(rèn)為事務(wù)3未完成,所以 select 的結(jié)果是肉蟹寶1。第2次 select 結(jié)果和第1次一樣,所以叫可重復(fù)讀。
小結(jié)
從上邊的描述中我們可以看出來,所謂的MVCC--多版本并發(fā)控制指的就是在使用read-commited、repeatable-read這兩種隔離級別的事務(wù)在執(zhí)行select操作時,訪問記錄的版本鏈的過程。這樣子就可以使不同的事務(wù)的讀-寫、寫-讀作并發(fā)執(zhí)行,從而提升系統(tǒng)性能。
read-commited、repeatable-read這兩個隔離級別的很大一個不同就是:生成ReadView的時機(jī)不同
read-commited 在每一次進(jìn)行select操作時都會生成一個ReadView,而repeatable-read只在第一次進(jìn)行普通select操作時生成一個ReadView,之后的查詢操作都重復(fù)使用這個ReadView
參考與感謝:
深入理解MYSQL的MVCC機(jī)制
javaguide
總結(jié)
以上是生活随笔為你收集整理的什么是MVCC,一文搞懂MySQL的MVCC机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阻塞和非阻塞IO,异步和同步IO
- 下一篇: 人手一份的中国城市道路名图鉴 | Alf