淺談MySQL中的MVCC

上一篇我們簡單聊了聊MySQL中LRU算法的實現,那么這一篇我們聊聊MySQL的另一個重點——MVCC(多版本并發控制);


一、什么是MVCC


???? MVCC,全稱Multi-Version Concurrency Control,即多版本并發控制。MVCC是一種并發控制的方法,一般在數據庫管理系統中,實現對數據庫的并發訪問,在編程語言中實現事務內存。


? ? MVCC在MySQL InnoDB中的實現主要是為了提高數據庫并發性能,用更好的方式去處理讀-寫沖突,做到即使有讀寫沖突時,也能做到不加鎖,非阻塞并發讀。


????舉個例子,程序員A正在讀數據庫中某些內容,而程序員B正在給這些內容做修改(假設是在一個事務內修改,大概持續10s左右),A在這10s內 則可能看到一個不一致的數據,在B沒有提交前,如何讓A能夠一直讀到的數據都是一致的呢


有幾種處理方法:

????第一種:基于鎖的并發控制,程序員B開始修改數據時,給這些數據加上鎖,程序員A這時再讀,就發現讀取不了,處于等待情況,只能等B操作完才能讀數據,這保證A不會讀到一個不一致的數據,但是這個會影響程序的運行效率。

????還有一種就是:MVCC,每個用戶連接數據庫時,看到的都是某一特定時刻的數據庫快照,在B的事務沒有提交之前,A始終讀到的是某一特定時刻的數據庫快照,不會讀到B事務中的數據修改情況,直到B事務提交,才會讀取B的修改內容。


二、什么是當前讀和快照讀


在學習MVCC多版本并發控制之前,我們必須先了解一下,什么是MySQL InnoDB下的當前讀和快照讀?

????當前讀:

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


? ? 快照讀:

? ? ? ? 像不加鎖的select * from 操作就是快照讀,即不加鎖的非阻塞讀,不涉及其他鎖之間的沖突;快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當前讀;之所以出現快照讀的情況,是基于提高并發性能的考慮,快照讀的實現是基于多版本并發控制,即MVCC,可以認為MVCC是行鎖的一個變種,但它在很多情況下,避免了加鎖操作,降低了開銷;既然是基于多版本,即快照讀可能讀到的并不一定是數據的最新版本,而有可能是之前的歷史版本。

?????說白了MVCC就是為了實現讀(select)-寫沖突不加鎖,而這個讀指的就是快照讀, 而非當前讀,當前讀實際上是一種加鎖的操作,是悲觀鎖的實現。


三、MVCC的實現原理

????MVCC的目的就是多版本并發控制,在數據庫中的實現,就是為了解決讀寫沖突,它的實現原理主要是依賴記錄中的 3個隱式字段,undo日志 ,Read View 來實現的。所以我們先來看看這個三個point的概念:

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

    • ?DB_TRX_ID:? ? 6byte,最近修改(修改/插入)事務ID:記錄創建這條記錄/最后一次修改該記錄的事務ID

    • DB_ROLL_PTR:? ?7byte,回滾指針,指向這條記錄的上一個版本(存儲于rollback segment里)

    • DB_ROW_ID:? ? ? ? 6byte,隱含的自增ID(隱藏主鍵),如果數據表沒有主鍵,InnoDB會自動以DB_ROW_ID產生一個聚集索引

    undo log日志主要分為兩種:

    ????

        • insert undo log:?代表事務在insert新記錄時產生的undo log, 只在事務回滾時需要,并且在事務提交后可以被立即丟棄


        • update undo log:? 事務在進行update或delete時產生的undo log; 不僅在事務回滾時需要,在快照讀(select,當讀的過程中有寫的事務開始和提交,會造成讀數據的臟讀、不可重復讀、幻讀等)時也需要;所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日志時,對應的日志才會被purge線程統一清除。


    purge:


    ????從前面的分析可以看出,為了實現InnoDB的MVCC機制,更新或者刪除操作都只是設置一下老記錄的deleted_bit,并不真正將過時的記錄刪除。????


    ???為了節省磁盤空間,InnoDB有專門的purge線程來清理deleted_bit為true的記錄。為了不影響MVCC的正常工作,purge線程自己也維護了一個read view(這個read view相當于系統中最老活躍事務的read view);


    ????如果某個記錄的deleted_bit為true,并且DB_TRX_ID相對于purge線程的read view可見,那么這條記錄一定是可以被安全清除的。


    舉個例子說明下:

    1、?比如一個有個事務插入person表插入了一條新記錄,記錄如下,name為Jerry, age為24歲,隱式主鍵是1,事務ID和回滾指針,我們假設為NULL


    2、現在來了一個事務1對該記錄的name做出了修改,改為Tom。

    ? ? 在事務1修改該行(記錄)數據時,數據庫會先對該行加排他鎖,然后把該行數據拷貝到undo log中,作為舊記錄,既在undo log中有當前行的拷貝副本;

    ????拷貝完畢后,修改該行name為Tom,并且修改隱藏字段的事務ID為當前事務1的ID, 我們默認從1開始,之后遞增,回滾指針指向拷貝到undo log的副本記錄,既表示我的上一個版本就是它



    3、又來了個事務2修改person表的同一個記錄,將age修改為30歲?

    ????在事務2修改該行數據時,數據庫也先為該行加鎖

    ????然后把該行數據拷貝到undo log中,作為舊記錄,發現該行記錄已經有undo log了,那么最新的舊數據作為鏈表的表頭,插在該行記錄的undo log最前面。

    ????修改該行age為30歲,并且修改隱藏字段的事務ID為當前事務2的ID, 那就是2,回滾指針指向剛剛拷貝到undo log的副本記錄

    ????事務提交,釋放鎖。



    四、ReadView

    ??? ReadView說白了就是一個數據結構,在SQL開始的時候被創建。是事務進行快照讀(select * from)操作的時候生產的讀視圖(Read View),在該事務執行的快照讀的那一刻,會生成事務系統當前的一個快照,記錄并維護系統當前活躍事務(未提交事務)的ID(當每個事務開啟時,都會被分配一個ID, 這個ID是遞增的,所以最新的事務,ID值越大)。


    ReadView{low_trx_id, up_trx_id, trx_ids}

      • low_trx_id表示該SQL啟動時,當前事務鏈表中最大的事務id編號,也就是最近創建的除自身以外最大事務編號;

      • up_trx_id表示該SQL啟動時,當前事務鏈表中最小的事務id編號,也就是當前系統中創建最早但還未提交的事務;

      • trx_ids表示所有事務鏈表中事務的id集合。

    上述3個成員組成了ReadView中的主要部分,簡單圖示如下:

    ????據上圖所示,所有數據行上DATA_TRX_ID小于up_trx_id的記錄,說明修改該行的事務在當前事務開啟之前都已經提交完成,所以對當前事務來說,都是可見的。而對于DATA_TRX_ID大于low_trx_id的記錄,說明修改該行記錄的事務在當前事務之后,所以對于當前事務來說是不可見的。

    ????注意,ReadView是與SQL綁定的,而并不是事務,所以即使在同一個事務中,每次SQL啟動時構造的ReadView的up_trx_id和low_trx_id也都是不一樣的,至于DATA_TRX_ID大于low_trx_id本身出現也只有當多個SQL并發的時候,在一個SQL構造完ReadView之后,另外一個SQL修改了數據后又進行了提交,對于這種情況,數據其實是不可見的。

    ????最后,至于位于(up_trx_id, low_trx_id)中間的事務是否可見,這個需要根據不同的事務隔離級別來確定。對于RC的事務隔離級別來說,對于事務執行過程中,已經提交的事務的數據,對當前事務是可見的,也就是說上述圖中,當前事務運行過程中,trx1~4中任意一個事務提交,對當前事務來說都是可見的;而對于RR隔離級別來說,事務啟動時,已經開始的事務鏈表中的事務的所有修改都是不可見的,所以在RR級別下,low_trx_id基本保持與up_trx_id相同的值即可。

    最后借用一種圖來解釋MySQL中實現的MVCC。

    注:第四節插圖來源網易數據庫和大數據資深專家蔣鴻翔分享的文章中插圖。原發表于其個人博客?。



    …………………………………分割線……………………………

    不積跬步,無以至千里;不積小流,無以成江海。

    關注我,每天分享一些小知識點。分享自己的小心得,包含但不限于初、中、高級面試題呦!!!


    我都墨跡這么半天了 ,你不點關注,不點贊,不收藏,還不轉發,你想干啥!!!!


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容