1. 程式人生 > 其它 >mysql-MVCC

mysql-MVCC

MVCC

多版本併發控制,通過資料行的多個版本管理實現資料庫的併發控制,使得在事務隔離級別下執行一致性讀有了保證,換言之,為了查詢一些正在被另一個事務更新的行,並且可以看到它們被更新之前的值,這樣在做查詢的時候就不用等待另一個事務釋放鎖。

快照讀 當前讀

主要是為了提高資料庫的併發效能,用更好的方式去處理讀寫衝突,做到即使有讀寫衝突,也能做到不加鎖非阻塞併發讀,讀是指快照讀,而非當前讀。當前讀是悲觀鎖的實現。

  • 快照讀:又叫一致性讀,讀取的是快照資料,不加鎖的簡單的select都屬於快照讀,是基於提高併發效能的考慮,快照讀的實現是基於MVCC,既然基於多版本的,快照讀可能讀取到的不一定是資料的最新版本,而有可能是歷史版本,前提隔離級別不是序列級別(退化成當前讀)
  • 當前讀: 讀取的是記錄的最新版本,讀取時還要保證其它併發事務不能修改當前記錄,會對讀取的記錄進行加鎖,加鎖的增刪蓋查都會進行當前讀。

再談隔離級別

在mysql中,預設的隔離級別是可重複讀,可以解決髒讀不可重複讀的問題,MVCC不採用鎖機制,通過樂觀鎖的方式解決不可重複讀和幻讀問題們可以在大多數情況下替代行級鎖,降低系統開銷。
undo log 的版本鏈:對於InnoDB儲存引擎的表,聚簇索引記錄中包含兩個必要的隱藏列

trx_id roll-pointer 1
每次事務對聚簇索引改動時就把該事務的事務ID賦值給它 每次對記錄進行改動時候,都會把舊的版本寫入undo log 2

注意不會出現在兩個事務中交叉更新同一條記錄(髒寫),使用鎖機制保證不會發生。每次對記錄進行改動,都會記錄一條undo日誌,每條undo日誌都有roll-pointer ,將undo日誌連起來形成一個連結串列。對該記錄每次更新後,都會將舊值放到一條undo日誌中,就算是該記錄的一箇舊版本,隨著更新次數的增多,所有的版本會被roll-pointer連結形成一個連結串列,稱之為版本鏈,版本鏈的頭結點就是當前記錄的最新的值。每個版本還包含生成該版本時對應的事務id。

MVCC實現原理之readview:隱藏欄位、undo log、readview

什麼是readview

在mvcc機制中,多個事務對同一個行記錄進行更新會產生多個歷史快照,這些歷史快照儲存在undo log中,如果一個事務想要查詢這個行記錄,需要讀取那個版本的記錄? readview就是事務在使用MVCC機制進行快照讀操作時產生的讀視,當事務啟動時,會生成資料系統當前的一個快照,innoDB為每個事務構造了一個數組,用來記錄並維護系統當前活躍(啟動但沒有提交)事務的id。

設計思路

使用RC RR隔離級別的事務,都必須保證讀到已經提交了的事務修改過的記錄,假如另一個事務已經修改了記錄但是尚未提交,是不能讀取最新版本的記錄的,因此需要判斷一下版本鏈中哪個版本是當前事務可見的。

creator-trx-id trx-ids up-limit-id 、low-limit-id
建立這個readview的事務id,只有對錶中記錄做改動時才會為事務分配事務id 生成readview時當前系統中活躍的讀寫事務id列表 活躍事務的最小id 、生成readview系統中應該分配給下一個事務的id值

訪問規則

  1. 如果被訪問版本的trx-id屬性值與readview中creator-trx-id相同,意味著當前事務在訪問它自己修改過的記錄,當前版本可以被當前事務訪問
  2. 如果被訪問版本的trx-id 小於up-limit-id ,表明生成該版本的事務在當前事務生成的readview前已經提交,該版本可以被當前事務訪問
  3. 如果被被訪問的tix-id大於或等於low-limit-id,表明生成該版本的事務在當前事務生成readview後才開啟,所以便版本不可以。。
  4. 如果 。。介於up -low之間,判斷trx-id是不是在trx-ids列表,如果在,說明該版本事務還是活躍的,不可以被訪問;如果不在,說明已經提交,可以被訪問。

操作流程

  1. 首先獲取事務自己的版本號(事務id)
  2. 獲取readview
  3. 查詢得到的資料,然後與readview中的事務版本進行比較
  4. 如果不符合readview規則,就需要從undo log中獲取歷史快照,
  5. 最後返回符合規則的資料。
    如果某個版本的資料對當前事務不可見時,就順著版本鏈找到下一個版本的資料,繼續按照上邊的步驟判斷可見性,直到版本鏈中的最後一個版本,MVCC是通過undo log +readview進行資料讀取,undo log 儲存了歷史快照,readview規則幫助判斷當前版本的資料是否可見。當隔離級別為RC時,每次select查詢都會重新獲取一次readview;當為RR時,只在第一次select時獲取readview ,後面的會複用這個readview。

如何解決幻讀?

對於快照讀: 核心在於readview原理:當隔離級別為RC時,每次select查詢都會重新獲取一次readview;當為RR時,只在第一次select時獲取readview ,後面的會複用這個readview。
對於當前讀: 加鎖,間隙鎖解決