一.背景
? ? 數(shù)據(jù)庫為了實現(xiàn)讀寫并行凫海,也就是說某一行數(shù)據(jù)在修改的時候會阻塞其他線程修改但不會阻塞讀,其他線程還可以讀男娄,因為大部分程序都是讀多于寫行贪,從而大大提升并發(fā)性能漾稀,使用MVCC (Multiversion Concurrency Control)即多版本并發(fā)控制方式實現(xiàn)。
? ? 大部分主流的數(shù)據(jù)庫比如mysql建瘫、oracle都是采用這種方式實現(xiàn)崭捍。
? ? 通過學習這種機制來更加了解數(shù)據(jù)庫底層實現(xiàn)細節(jié)同時也是學習這種設計思想。
二.DML整體處理流程
上面流程中看出來:
一筆update請求在innodb中主要做以下操作:
1>.收到當前讀請求加鎖并返回數(shù)據(jù)給server啰脚。
2>.server過濾完數(shù)據(jù)殷蛇,對滿足條件的數(shù)據(jù)發(fā)起更新。
3>.innodb收到發(fā)起更新操作并不是修改原數(shù)據(jù)橄浓,而是copy一份出來粒梦,寫入undo緩沖中,異步線程刷入位于共享表空間中undo文件荸实。
4>.insert一條新紀錄到redo日志到LRU列表大約37%處匀们,Page Clearner線程會定時通過checkpoint機制將臟頁刷新回磁盤。
5>.server發(fā)起commit
寫binlog到緩沖區(qū)准给,默認sync_binlog=0則異步刷盤泄朴,發(fā)提交請求
innodb釋放鎖記錄X鎖,釋放頁表庫的IX鎖露氮,
undo日志設置刪除標記為1祖灰,會被purge線程定時清理掉。
寫提交記錄到history list畔规,用于回放redo日志判斷是否提交(redo因為記錄的是物理日志所以是冪等的局扶,重放多次結果都一樣)。
注:因為redo日志在事務未提交之前已經(jīng)寫好了叁扫,所以很大的事物提交也是很快的三妈,大部分工作都在事務進行中完成了。
Q:讀線程怎么讀到原來的數(shù)據(jù)陌兑?下面來看沈跨。
三.MVCC實現(xiàn)
接著上面的問題讀線程怎么讀到原來的數(shù)據(jù),來實現(xiàn)事務隔離級別呢由捎?
Innodb每行記錄都有兩個隱藏字段:
DB_TRX_ID?:事務id兔综,數(shù)據(jù)庫為事務維護一個自增的序列,存該序列值狞玛。
DB_ROLL_PTR:回滾指針软驰,用于update查詢或回滾尋找undo版本數(shù)據(jù)。
當插入一條數(shù)據(jù)回滾段指針為NULL心肪,事務id為當前事務id锭亏,因為這個不需要提供給其他線程讀。
更新事務:
1.當前讀返回并加鎖硬鞍。
2.把該行的邏輯值copy到undo緩沖區(qū)
3.?插入一條新記錄慧瘤,填寫事務編號和回滾指針
4.記錄redo日志戴已。
MySQL就是根據(jù)事務ID判斷記錄是否可見,如果不可見繼續(xù)按照DB_ROLL_PTR繼續(xù)回溯查找锅减。
四.MVCC如何判斷記錄可見
可見性分析:
假設原數(shù)據(jù)如下:
原數(shù)據(jù):(新插入已提交)
idnameDB_TRX_IDDB_ROLL_PTR
? ? ? 1? ? mbj? ? ? ? 1
執(zhí)行update操作 :update t_user set name='mbj1' where id=1;
undo日志:(原數(shù)據(jù)copy一份出來至undo緩存中)
地址idnameDB_TRX_IDDB_ROLL_PTR
? ?A100? ? ? 1? ? mbj? ? ? ? 1
LRU數(shù)據(jù):(插入一條新數(shù)據(jù)至數(shù)據(jù)緩存池糖儡,當前事務正在修改未提交)
idnameDB_TRX_IDDB_ROLL_PTR
? ? ? 1? ?mbj1? ? ? ?2? ? ? A100
? ? 不同的隔離級別,數(shù)據(jù)的可見度不同怔匣。 RC隔離級別下允許幻讀握联,期間新插入的數(shù)據(jù)是可以看到,RR下不允許每瞒。
innodb為了能支持多個事務同時查詢金闽,會將當前系統(tǒng)中的所有的進行中的事務拷貝到一個列表中(read view),
RC隔離級別剿骨,在事務中的每個語句開始時代芜,copy活躍事務到read view。
RR隔離級別懦砂,在每個事務開始的時候蜒犯,copy活躍事務到read view。
假設此時事務2:update t_user set name='mbj2' where id=1;(事務1未提交)
事務2 read view中有1荞膘。
獲取數(shù)據(jù)DB_TRX_ID=1罚随,因為1在read view中所以不展示,找到undo回滾數(shù)據(jù)DB_TRX_ID=0羽资,同時DB_TRX_ID<當前事務id淘菩,也就是2.都滿足,則返回數(shù)據(jù)屠升。