1. 程式人生 > 其它 >MVVC(多版本併發控制)

MVVC(多版本併發控制)

1、MVCC

​   MVCC,全稱Multi-Version Concurrency Control,即多版本併發控制。MVCC是一種併發控制的方法,一般在資料庫管理系統中,實現對資料庫的併發訪問,在程式語言中實現事務記憶體。MVCC在MySQL InnoDB中的實現主要是為了提高資料庫併發效能,用更好的方式去處理讀寫衝突,做到即使有讀寫衝突時,也能做到不加鎖,非阻塞併發讀。

2、當前讀

​  像select lock in share mode(共享鎖), select for update ; update, insert ,delete(排他鎖)這些操作都是一種當前讀,為什麼叫當前讀?就是它讀取的是記錄的最新版本,讀取時還要保證其他併發事務不能修改當前記錄,會對讀取的記錄進行加鎖。

3、快照讀(提高資料庫的併發查詢能力)

​  像不加鎖的select操作就是快照讀,即不加鎖的非阻塞讀;快照讀的前提是隔離級別不是序列級別,序列級別下的快照讀會退化成當前讀;之所以出現快照讀的情況,是基於提高併發效能的考慮,快照讀的實現是基於多版本併發控制,即MVCC,可以認為MVCC是行鎖的一個變種,但它在很多情況下,避免了加鎖操作,降低了開銷;既然是基於多版本,即快照讀可能讀到的並不一定是資料的最新版本,而有可能是之前的歷史版本

4、當前讀、快照讀、MVCC關係

​  MVCC多版本併發控制指的是維持一個數據的多個版本,使得讀寫操作沒有衝突,快照讀是MySQL為實現MVCC的一個非阻塞讀功能。MVCC模組在MySQL中的具體實現是由三個隱式欄位,undo日誌、read view三個元件來實現的。

5、MVCC解決的問題

​   資料庫併發場景有三種,分別為:

​   1、讀讀:不存在任何問題,也不需要併發控制

​   2、讀寫:有執行緒安全問題,可能會造成事務隔離性問題,可能遇到髒讀、幻讀、不可重複讀

​   3、寫寫:有執行緒安全問題,可能存在更新丟失問題

​  MVCC是一種用來解決讀寫衝突的無鎖併發控制,也就是為事務分配單項增長的時間戳,為每個修改儲存一個版本,版本與事務時間戳關聯,讀操作只讀該事務開始前的資料庫的快照,所以MVCC可以為資料庫解決一下問題:

​   1、在併發讀寫資料庫時,可以做到在讀操作時不用阻塞寫操作,寫操作也不用阻塞讀操作,提高了資料庫併發讀寫的效能

​   2、解決髒讀、幻讀、不可重複讀等事務隔離問題,但是不能解決更新丟失問題

6、MVCC實現原理

​   mvcc的實現原理主要依賴於記錄中的三個隱藏欄位,undolog,read view來實現的。

​   隱藏欄位每行記錄除了我們自定義的欄位外,還有資料庫隱式定義的DBTRXID,DBROLLPTR,DBROWID等欄位

​     DBTRXID:6位元組,最近修改事務id,記錄建立這條記錄或者最後一次修改該記錄的事務id

​     DBROLLPTR:7位元組,回滾指標,指向這條記錄的上一個版本,用於配合undolog,指向上一個舊版本

​     DBROWJD:6位元組,隱藏的主鍵,如果資料表沒有主鍵,那麼innodb會自動生成一個6位元組的row_id

    DBROWID是資料庫預設為該行記錄生成的唯一隱式主鍵,DBTRXID是當前操作該記錄的事務ID,DBROLLPTR是一個回滾指標,用於配合undo日誌,指向上一個舊版本

​   undo logundolog被稱之為回滾日誌,表示在進行insert,delete,update操作的時候產生的方便回滾的日誌

​     當進行insert操作的時候,產生的undolog只在事務回滾的時候需要,並且在事務提交之後可以被立刻丟棄;當進行update和delete操作的時候,產生的undolog不僅僅在事務回滾的時候需要,在快照讀的時候也需要,所以不能隨便刪除,只有在快照讀或事務回滾不涉及該日誌時,對應的日誌才會被purge執行緒統一清除(當資料發生更新和刪除操作的時候都只是設定一下老記錄的deletedbit,並不是真正的將過時的記錄刪除,因為為了節省磁碟空間,innodb有專門的purge執行緒來清除deletedbit為true的記錄,如果某個記錄的deleted_id為true,並且DBTRXID相對於purge執行緒的read view 可見,那麼這條記錄一定時可以被清除的)。不同事務或者相同事務的對同一記錄的修改,會導致該記錄的undolog生成一條記錄版本線性表,即連結串列,undolog的鏈首就是最新的舊記錄,鏈尾就是最早的舊記錄。

​   Read View:Read View是事務進行快照讀操作的時候生產的讀檢視,在該事務執行快照讀的那一刻,會生成一個數據系統當前的快照,記錄並維護系統當前活躍事務的id,事務的id值是遞增的。

    其實Read View的最大作用是用來做可見性判斷的,也就是說當某個事務在執行快照讀的時候,對該記錄建立一個Read View的檢視,把它當作條件去判斷當前事務能夠看到哪個版本的資料,有可能讀取到的是最新的資料,也有可能讀取的是當前行記錄的undolog中某個版本的資料

​    Read View遵循的可見性演算法主要是將要被修改的資料的最新記錄中的DBTRXID(當前事務id)取出來,與系統當前其他活躍事務的id去對比,如果DBTRXID跟Read View的屬性做了比較,不符合可見性,那麼就通過DBROLLPTR回滾指標去取出undolog中的DBTRXID做比較,即遍歷連結串列中的DBTRXID,直到找到滿足條件的DBTRXID,這個DBTRXID所在的舊記錄就是當前事務能看到的最新老版本資料。

​     Read View的可見性規則如下所示,首先要知道Read View中的三個全域性屬性:

​       trx_list:一個數值列表,用來維護Read View生成時刻系統正活躍的事務ID(1,2,3)

​       uplimitid:記錄trx_list列表中事務ID最小的ID(1)

​       lowlimitid:Read View生成時刻系統尚未分配的下一個事務ID,(4)

​     具體的比較規則如下:

​     1、首先比較DBTRXID < uplimitid,如果小於,則當前事務能看到DBTRXID所在的記錄,如果大於等於進入下一個判斷

​     2、接下來判斷DBTRXID >= lowlimitid,如果大於等於則代表DBTRXID所在的記錄在Read View生成後才出現的,那麼對於當前事務肯定不可見,如果小於,則進入下一步判斷

​     3、判斷DBTRXID是否在活躍事務中,如果在,則代表在Read View生成時刻,這個事務還是活躍狀態,還沒有commit,修改的資料,當前事務也是看不到,如果不在,則說明這個事務在Read View生成之前就已經開始commit,那麼修改的結果是能夠看見的。

7、RC、RR級別下的InnoDB快照讀有什麼不同

​   因為Read View生成時機的不同,從而造成RC、RR級別下快照讀的結果的不同

​   1、在RR級別下的某個事務的對某條記錄的第一次快照讀會建立一個快照即Read View,將當前系統活躍的其他事務記錄起來,此後在呼叫快照讀的時候,還是使用的是同一個Read View,所以只要當前事務在其他事務提交更新之前使用過快照讀,那麼之後的快照讀使用的都是同一個Read View,所以對之後的修改不可見

​   2、在RR級別下,快照讀生成Read View時,Read View會記錄此時所有其他活動和事務的快照,這些事務的修改對於當前事務都是不可見的,而早於Read View建立的事務所做的修改均是可見

​   3、在RC級別下,事務中,每次快照讀都會新生成一個快照和Read View,這就是我們在RC級別下的事務中可以看到別的事務提交的更新的原因。

總結:在RC隔離級別下,是每個快照讀都會生成並獲取最新的Read View,而在RR隔離級別下,則是同一個事務中的第一個快照讀才會建立Read View,之後的快照讀獲取的都是同一個Read View.