MVCC
就是多版本并發控制,MVCC 是一種并發控制的方法,一般在資料庫管理系統中,實作對資料庫的并發訪問,
為什么需要MVCC呢?資料庫通常使用鎖來實作隔離性,最原生的鎖,鎖住一個資源后會禁止其他任何執行緒訪問同一個資源,但是很多應用的一個特點都是讀多寫少的場景,很多資料的讀取次數遠大于修改的次數,而讀取資料間互相排斥顯得不是很必要,所以就使用了一種讀寫鎖的方法,讀鎖和讀鎖之間不互斥,而寫鎖和寫鎖、讀鎖都互斥,這樣就很大提升了系統的并發能力,之后人們發現并發讀還是不夠,又提出了能不能讓讀寫之間也不沖突的方法,就是讀取資料時通過一種類似快照的方式將資料保存下來,這樣讀鎖就和寫鎖不沖突了,不同的事務session會看到自己特定版本的資料,當然快照是一種概念模型,不同的資料庫可能用不同的方式來實作這種功能,
理解MVCC
什么是MVCC
全稱Multi-Version Concurrency Control,即多版本并發控制,主要是為了提高資料庫的并發性能,PS:基于InnoDB引擎的默認事務機制可重復讀來講的,因為Mylsam不支持事務,
同一行資料平時發生讀寫請求時,會上鎖阻塞住,當讀為快照讀時mvcc用更好的方式去處理讀—寫請求,做到在發生讀—寫請求沖突時不用加鎖,
select .. for update 即當前讀是一種加鎖操作,是悲觀鎖,
那它到底是怎么做到讀—寫不用加鎖的,快照讀和當前讀又是什么,
快照讀與當前讀的區別:
當前讀:它讀取的資料都是當前最新的資料,會對讀取到的資料進行加鎖,防止其他事務修改其資料,是悲觀鎖的一種,這里不做展開,
例如如下操作:
- select fro update
- update
- insert
- delete
- select lock in share mode
- 事務隔離級別為串行化時都加鎖
快照讀:最基礎的不加鎖的select操作
快照讀的實作是基于多版本并發控制,即MVCC,既然是多版本,那么快照讀讀到的資料不一定是當前最新的資料,有可能是之前歷史版本的資料,
快照讀與mvcc的關系
MVCCC是“維持一個資料的多個版本,使讀寫操作沒有沖突”的一個抽象概念,
這個概念需要具體功能去實作,這個具體實作就是快照讀,
資料庫并發場景
讀-讀:不存在任何問題,也不需要并發控制讀-寫:有執行緒安全問題,可能會造成事務隔離性問題,可能遇到臟讀,幻讀,不可重復讀寫-寫:有執行緒安全問題,可能會存在更新丟失問題,比如第一類更新丟失,第二類更新丟失
MVCC解決并發哪些問題?
mvcc用來解決讀—寫沖突的無鎖并發控制,就是為事務分配單向增長的時間戳,為每個資料修改保存一個版本,版本與事務時間戳相關聯,
讀操作只讀取該事務開始前的資料庫快照,
解決問題如下:
并發讀-寫時:可以做到讀操作不阻塞寫操作,同時寫操作也不會阻塞讀操作,- 解決
臟讀、大部分幻讀、不可重復讀等事務隔離問題,但不能解決上面的寫-寫 更新丟失問題,(PS:不能完全解決幻讀)
MVCC的實作原理
它的實作原理主要是版本鏈,undo日志 ,Read View 來實作的
版本鏈:
在InnoDB引擎表中,它的聚簇索引記錄中有兩個必要的隱藏列:
-
trx_id這個id用來存盤的每次對某條聚簇索引記錄進行修改的時候的事務id, -
roll_pointe每次對哪條聚簇索引記錄有修改的時候,都會把老版本寫入undo日志中,這個roll_pointer就是存了一個指標,它指向這條聚簇索引記錄的上一個版本的位置,通過它來獲得上一個版本的記錄資訊,(注意插入操作的undo日志沒有這個屬性,因為它沒有老版本)(實作版本鏈的關鍵) -
row_id,隱含的自增ID(隱藏主鍵),如果資料表沒有主鍵,InnoDB會自動以db_row_id產生一個聚簇索引,
- 實際還有一個
洗掉flag隱藏欄位, 記錄被更新或洗掉并不代表真的洗掉,而是洗掉flag變了

如上圖,row_id是資料庫默認為該行記錄生成的唯一隱式主鍵,trx_id是當前操作該記錄的事務ID,而roll_pointer是一個回滾指標,用于配合undo日志,指向上一個舊版本,
若此時執行下列陳述句,

更新后的版本鏈:

undo日志
Undo log 主要用于記錄資料被修改之前的日志,在表資訊修改之前先會把資料拷貝到undo log里,
當事務進行回滾時可以通過undo log 里的日志進行資料還原,
Undo log 的用途
- 保證
事務進行rollback時的原子性和一致性,當事務進行回滾的時候可以用undo log的資料進行恢復, - 用于MVCC
快照讀的資料,在MVCC多版本控制中,通過讀取undo log的歷史版本資料可以實作不同事務版本號都擁有自己獨立的快照資料版本,
undo log主要分為兩種:
-
insert undo log
代表事務在insert新記錄時產生的undo log , 只在事務回滾時需要,并且在事務提交后可以被立即丟棄
-
update undo log(主要)
事務在進行update或delete時產生的undo log ; 不僅在事務回滾時需要,在快照讀時也需要;
所以不能隨便洗掉,只有在快速讀或事務回滾不涉及該日志時,對應的日志才會被purge執行緒統一清除
Read View(讀視圖)
事務進行快照讀操作的時候生產的讀視圖(Read View),在該事務執行的快照讀的那一刻,會生成資料庫系統當前的一個快照,
記錄并維護系統當前活躍事務的ID(沒有commit,當每個事務開啟時,都會被分配一個ID, 這個ID是遞增的,所以越新的事務,ID值越大),是系統中當前不應該被本事務看到的其他事務id串列,
Read View主要是用來做可見性判斷的, 即當我們某個事務執行快照讀的時候,對該記錄創建一個Read View讀視圖,把它比作條件用來判斷當前事務能夠看到哪個版本的資料,既可能是當前最新的資料,也有可能是該行記錄的undo log里面的某個版本的資料,
Read View幾個屬性
trx_ids: 當前系統活躍(未提交)事務版本號集合,low_limit_id: 創建當前read view 時“當前系統最大事務版本號+1”,up_limit_id: 創建當前read view 時“系統正處于活躍事務最小版本號”creator_trx_id: 創建當前read view的事務版本號;
一個事務去訪問記錄的時候,除了自己的更新記錄總是可見之外,還有這幾種情況:
- 如果記錄的 trx_id 值小于 Read View 中的
min_trx_id值,表示這個版本的記錄是在創建 Read View 前已經提交的事務生成的,所以該版本的記錄對當前事務可見, - 如果記錄的 trx_id 值大于等于 Read View 中的
max_trx_id值,表示這個版本的記錄是在創建 Read View 后才啟動的事務生成的,所以該版本的記錄對當前事務不可見, - 如果記錄的 trx_id 值在 Read View 的
min_trx_id和max_trx_id之間,需要判斷trx_id是否在m_ids串列中:- 如果記錄的 trx_id 在
m_ids串列中,表示生成該版本記錄的活躍事務依然活躍著(還沒提交事務),所以該版本的記錄對當前事務不可見, - 如果記錄的 trx_id 不在
m_ids串列中,表示生成該版本記錄的活躍事務已經被提交,所以該版本的記錄對當前事務可見,
- 如果記錄的 trx_id 在
這種通過「版本鏈」來控制并發事務訪問同一個記錄時的行為就叫 MVCC(多版本并發控制),
Read View可見性判斷條件
-
db_trx_id<up_limit_id||db_trx_id==creator_trx_id(顯示)如果資料事務ID小于read view中的
最小活躍事務ID,則可以肯定該資料是在當前事務啟之前就已經存在了的,所以可以顯示,或者資料的
事務ID等于creator_trx_id,那么說明這個資料就是當前事務自己生成的,自己生成的資料自己當然能看見,所以這種情況下此資料也是可以顯示的, -
db_trx_id>=low_limit_id(不顯示)如果資料事務ID大于read view 中的當前系統的
最大事務ID,則說明該資料是在當前read view 創建之后才產生的,所以資料不顯示,如果小于則進入下一個判斷 -
db_trx_id是否在活躍事務(trx_ids)中不存在:則說明read view產生的時候事務已經commit了,這種情況資料則可以顯示,已存在:則代表我Read View生成時刻,你這個事務還在活躍,還沒有Commit,你修改的資料,我當前事務也是看不見的,
MVCC和事務隔離級別
上面所講的Read View用于支持RC(Read Committed,讀提交)和RR(Repeatable Read,可重復讀)隔離級別的實作,
RR、RC生成時機
RC隔離級別下,是每個快照讀都會生成并獲取最新的Read View;- 而在
RR隔離級別下,則是同一個事務中的第一個快照讀才會創建Read View,之后的快照讀獲取的都是同一個Read View,之后的查詢就不會重復生成了,所以一個事務的查詢結果每次都是一樣的,
解決幻讀問題
快照讀:通過MVCC來進行控制的,不用加鎖,按照MVCC中規定的“語法”進行增刪改查等操作,以避免幻讀,沒有完全解決,當前讀:通過next-key鎖(行鎖+gap鎖)來解決問題的,
RC、RR級別下的InnoDB快照讀區別
- 在RR級別下的某個事務的對某條記錄的第一次快照讀會創建一個快照及Read View, 將當前系統活躍的其他事務記錄起來,此后在呼叫快照讀的時候,還是使用的是同一個Read View,所以只要當前事務在其他事務提交更新之前使用過快照讀,那么之后的快照讀使用的都是同一個Read View,所以對之后的修改不可見;
- 即RR級別下,快照讀生成Read View時,Read View會記錄此時所有其他活動事務的快照,這些事務的修改對于當前事務都是不可見的,而早于Read View創建的事務所做的修改均是可見
- 而在RC級別下的,事務中,每次快照讀都會新生成一個快照和Read View, 這就是我們在RC級別下的事務中可以看到別的事務提交的更新的原因
總結
從以上的描述中我們可以看出來,所謂的MVCC指的就是在使用READ COMMITTD、REPEATABLE READ這兩種隔離級別的事務在執行普通的SEELCT操作時訪問記錄的版本鏈的程序,這樣子可以使不同事務的讀-寫、寫-讀操作并發執行,從而提升系統性能,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/547895.html
標籤:其他
上一篇:多執行緒
