什么是MVCC
MVCC全稱是Multi-Version Concurrency Control,即多版本並發控制,主要是為了提高數據庫的並發讀寫性能。
當我們並發讀寫同一行數據的時候,為了防止出錯,需要對數據進行加鎖操作,但這並不是一個高效的操作,很容易造成操作請求阻塞超時。而MVCC采用了更好的方式去處理並發讀寫請求,做到在發生讀寫請求沖突時不用加鎖。這個讀是指的快照讀,而不是當前讀。
那么什么是當前讀,什么又是快照讀?我們下面來學習一下。
當前讀
當前讀就是讀取數據庫記錄的最新版本,會對當前讀取的數據進行加鎖,防止其他事務修改數據,是悲觀鎖的一種操作。
以下幾種情況是當前讀:
-
select 語句加鎖是當前讀
# 共享鎖 select a from t where id = 1 lock in share mode; #排他鎖 select a from t where id = 1 for update;
-
update 語句是當前讀
update t set a = a + 1;
快照讀
快照讀的實現是基於多版本並發控制,即MVCC,快照讀讀到的數據不一定是當前最新的數據,有可能是之前歷史版本的數據。
以下幾種情況是快照讀:
-
在默認隔離級別下,select 語句默認是快照讀
select a from t where id = 1;
快照讀與MVCC的關系
MVCC是”維持一個數據的多個版本,使讀寫操作沒有沖突”的一個抽象概念。
這個概念需要具體功能去實現,這個具體實現就是快照讀。
MVCC解決並發哪些問題
MVCC用來解決讀-寫沖突的無鎖並發控制,就是為事務分配單向增長的時間戳。為每個數據修改保存一個版本,版本與事務時間戳相關聯。讀操作只讀取該事務開始前的數據庫快照。
解決問題如下:
-
並發讀-寫時:可以做到讀操作不阻塞寫操作,同時寫操作也不會阻塞讀操作。
-
解決臟讀、幻讀、不可重復讀等事務隔離問題,但不能解決寫-寫更新丟失問題。因此需要下面提高並發性能的組合:
- MVCC + 悲觀鎖:MVCC解決讀寫沖突,悲觀鎖解決寫寫沖突
- MVCC + 樂觀鎖:MVCC解決讀寫沖突,樂觀鎖解決寫寫沖突
MVCC的實現原理
MVCC主要是版本鏈,undo日志 ,Read View 來實現的
版本鏈 & undo日志
我們數據庫中的每行數據都有如下幾個隱藏字段:
字段名 | 占用空間 | 說明 |
---|---|---|
trx_id | 6byte | 最近修改(修改/插入)事務ID:記錄創建這條記錄/最后一次修改該記錄的事務ID |
roll_pointer | 7byte | 回滾指針,指向這條記錄的上一個版本(存儲於rollback segment里) |
row_id | 6byte | 隱含的自增ID(隱藏主鍵),如果數據表沒有主鍵,InnoDB會自動以db_row_id產生一個聚簇索引 |
版本鏈的結構如下圖所示,每次修改都會在這個鏈條后面增加一個版本,trx_id
就代表最新這條記錄的ID。而歷史數據都會被存入undo日志,通過回滾指針roll_pointer
可以找到數據行的前一個版本,以供事務回滾時使用。
Read View
事務進行快照讀操作的時候生產的讀視圖(Read View),在該事務執行快照讀的那一刻,會生成數據庫系統當前的一個快照。通過Read View,事務可以判斷版本鏈中的哪個版本是可用的。
Read View中保存的字段信息如下:
字段名 | 說明 |
---|---|
m_ids | 表示在生成Read View時,當前系統中活躍的讀寫事務的事務ID列表 |
min_trx_id | 表示在生成Read View時,當前系統中活躍的讀寫事務中最小的事務ID,也就是m_ids中的最小值 |
max_trx_id | 表示生成Read View時,系統中應該分配給下一個事務的ID值 |
creator_trx_id | 表示生成該Read View的事務的事務ID |
那么Read View是如何判斷版本鏈中的哪個版本是可用的呢?通過以下4個條件就可以判斷:
trx_id
==creator_trx_id
:可以訪問這個版本trx_id
<min_trx_id
:可以訪問這個版本trx_id
>max_trx_id
:不可以訪問這個版本min_trx_id
<=trx_id
<=max_trx_id
:如果trx_id
在m_ids
中是不可以訪問這個版本的,反之可以訪問
MVCC如何實現RC和RR
我們在上一篇中講過RC(Read Committed,讀提交)和RR(Repeatable Read,可重復讀)隔離級別的定義,這兩個隔離級別的實現都離不開MVCC。
RR、RC生成Read View的時機
- RC隔離級別下,每個快照讀都會生成並獲取最新的Read View,因此可能出現在同一個事務中兩次查詢的結果不一致的情況
- RR隔離級別下,則是同一個事務中的第一個快照讀才會創建Read View,之后的快照讀獲取的都是同一個Read View,之后的查詢就不會重復生成了,所以一個事務的查詢結果每次都是一樣的
解決幻讀問題
- 快照讀:通過MVCC來進行控制的,不用加鎖。按照MVCC中規定的“語法”進行增刪改查等操作,以避免幻讀。
- 當前讀:通過next-key鎖(行鎖+gap鎖)來解決問題的。
總結
從以上的描述中,我們可以看出MVCC指的就是在使用RC、RR這兩種隔離級別的事務在執行普通的SEELCT操作時訪問記錄的版本鏈的過程,MVCC可以使不同事務的讀-寫、寫-讀操作並發執行,從而提升系統性能。