MVCC多版本控制太難懂了,看了不少文章資料終於弄清楚了一丟丟

MVCC

1.1 什麼是MVCC

MVCC,全稱 Multi_Version Concurrency Control ,即多版本併發控制。

MVCC是一種併發控制的方法,一般在數據庫管理系統中,實現對數據庫的併發訪問,在編程語言中實現事務內存。

  • 相對於傳統的基於鎖的併發控制主要特點是即使有讀寫衝突時,也能做到不加鎖,非阻塞併發讀;
    這種特性對於讀多寫少的場景,提高了高數據庫併發性能,因此大部分關係型數據庫(如MySQL,Oracle,PostgreSQL)都實現了MVCC。

  • 鎖機制可以控制併發操作,但是其系統開銷較大,而MVCC可以在大多數情況下代替行級鎖,使用MVCC,能降低其系統開銷。

  • MVCC是通過保存數據在某個時間點的快照來實現的。MVCC並沒有一個統一的實現標準,不同存儲引擎的MVCC實現是不同的,典型的有樂觀併發控制和悲觀併發控制。

1.2 MVCC優缺點

多版本併發控制(MVCC)是一種用來解決讀-寫衝突的無鎖併發控制,也就是爲事務分配單向增長的時間戳,爲每個修改保存一個版本,版本與事務時間戳關聯,讀操作只讀該事務開始前的數據庫的快照。
所以MVCC可以爲數據庫解決以下問題:

  • 在併發讀寫數據庫時,MVCC在大多數情況下代替了行鎖,實現了對讀的非阻塞,讀不加鎖,讀寫不衝突,提高了數據庫併發讀寫的性能;

  • 同時還可以解決髒讀,幻讀,不可重複讀等事務隔離問題,但不能解決更新丟失問題。

缺點是每行記錄都需要額外的存儲空間,需要做更多的行維護和檢查工作。

髒讀:在一個事務中讀取到另一個事務沒有提交的數據; 不可重複讀:在一個事務中,兩次查詢的結果不一致(針對的update操作); 虛讀(幻讀):在一個事務中,兩次查詢的結果不一致(針對的insert操作)。

1.2 MVCC具體實現

主要是依賴記錄中的 3個隱式字段,undo日誌 ,Read View 來實現的。

1.2.1 隱式字段

每行記錄除了我們自定義的字段外,還有數據庫隱式定義的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID三個字段

  • 6字節的事務ID(DB_TRX_ID)字段:
    記錄創建這條記錄/最後一次修改該記錄的事務ID
  • 7字節的回滾指針(DB_ROLL_PTR)字段:
    指寫入回滾段(rollback segment)的 undo log record (撤銷日誌記錄記錄)。
    如果一行記錄被更新, 則 undo log record 包含 ‘重建該行記錄被更新之前內容’ 所必須的信息。
  • 6字節的行標識 DB_ROW_ID字段:
    插入新行而單調增加;
    當由innodb自動產生聚集索引時,聚集索引會包括這個行ID的值,否則這個行ID不會出現在任何索引中。
  • 如果我們的表中沒有主鍵或合適的唯一索引, 也就是無法生成聚簇索引的時候, InnoDB會幫我們自動生成聚集索引, 但聚簇索引會使用DB_ROW_ID的值來作爲主鍵; 如果我們有自己的主鍵或者合適的唯一索引, 那麼聚簇索引中也就不會包含 DB_ROW_ID。
  • 此外,刪除在內部被視爲更新,在該更新中,行中的刪除flag隱藏字段被設置爲將其標記爲已刪除。

1.2.2 undo日誌

包括以下兩種:

  • insert undo log
    代表事務在insert新記錄時產生的undo log,;
    只在事務回滾時需要,並且在事務提交後可以被立即丟棄
  • update undo log
    事務在進行update或delete時產生的undo log;
    不僅在事務回滾時需要,在快照讀時也需要;所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日誌時,對應的日誌纔會被purge線程統一清除

1.2.3 read_view

read_view:每個事務在開始的時候都會根據當前系統的活躍事務鏈表創建一個read_view,記錄並維護系統當前活躍事務的ID(當每個事務開啓時,都會被分配一個ID, 這個ID是遞增的,所以最新的事務,ID值越大)。

對於read view快照的生成時機, 也非常關鍵, 正是因爲生成時機的不同, 造成了RC,RR兩種隔離級別的不同可見性;

  • 在innodb中(默認repeatable read級別), 事務在begin/start transaction之後的第一條select讀操作後, 會創建一個快照(read view), 將當前系統中活躍的其他事務記錄記錄起來;
  • 在innodb中(默認repeatable committed級別), 事務中每條select語句都會創建一個快照(read view);

在read view中有一個可見性比較算法

首先了解read_view數據結構分爲三個部分:
(1)當前活躍的事務列表
(2)Tmin,活躍事務的最小值
(3)Tmax,系統中最大事務ID(不管事務是否提交)加上1
再看以下流程圖:
在這裏插入圖片描述

2.1 測試示例

2.1.1 準備數據

  1. 創建測試表test_mvcc
mysql> create table test_mvcc( name varchar(8),age int);
  1. 插入測試數據
insert into table test_mvcc values('wj',23);
insert into table test_mvcc values('wj2',24);
insert into table test_mvcc values('wj3',25);

2.1.2 測試驗證

開了三個窗口連接MySQL進行測試:

  1. 窗口一 未作任何操作
  2. 窗口二 update修改了數據,但未commit
  3. 窗口三 select數據
    在這裏插入圖片描述

2.2.3 分析

可以看到 :
1.Trx id 均爲76332(未有事務commit數據)
2.第一個事務 query id 爲 71,第二個事務 query id 爲 74,第三個事務 query id 爲 74;
3.只有update的事務2能看到更新的數據,其他事務不可見。

事務1 :tid<tmin,此時事務2尚未commit,所以讀取未被事務2修改的數據
事務2 :tid==當前事務id,所以讀取到被修改後的數據
事務3 :tmin<tid<tmax,且tid在活躍事務列表中,所以順着回滾指針跳轉到上一行記錄,即取出未修改的數據進行讀取;

參考

《MVCC多版本控制》:一個旺財和小強對數據操作的故事,有趣易懂
《MVCC多版本併發控制》:較詳細的解釋,不難理解
《MVCC多版本併發控制機制》:從增刪改查方面簡單講解
《MySQL中InnoDB的多版本併發控制(MVCC)》:通過表格講解示例,容易理解
《InnoDB存儲引擎MVCC的工作原理》:直接從客戶端分析
《select for update引發死鎖分析》:詳細事例分析
《MySQL-InnoDB-MVCC多版本併發控制》:主要注重概念講解,屬於筆記類

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章