概念
Multi Version Concurrency Control, 用於數據庫的併發訪問控制
MVCC在mysql innoDB中的實現主要是爲了提高數據庫併發性能, 用更好的方式去處理讀-寫衝突, 實現讀寫衝突不加鎖, 非阻塞併發讀寫
每一次的數據修改都會將歷史記錄保存在Undo log裏, 讀數據採用快照讀的方式, 這樣就不會有阻塞了
什麼是當前讀和快照讀
當前讀
共享鎖、排他鎖 都是保證其他事務不能併發修改當前記錄, 所以讀取的肯定是最新紀錄
快照讀
簡單理解就是 不加鎖的select、快照讀的前提是基於多版本控制, 快照讀讀取的不一定是最新數據, 有可能是某個版本的數據
MVCC能解決什麼問題
數據庫併發場景有三種,分別爲:
讀-讀
:不存在任何問題,也不需要併發控制讀-寫
:有線程安全問題,可能會造成事務隔離性問題,可能遇到髒讀,幻讀,不可重複讀寫-寫
:有線程安全問題,可能會存在更新丟失問題,比如第一類更新丟失,第二類更新丟失
MVCC 帶來的好處是?
多版本併發控制(MVCC)是一種用來解決讀-寫衝突的無鎖併發控制,也就是爲事務分配單向增長的時間戳,爲每個修改保存一個版本,版本與事務時間戳關聯,讀操作只讀該事務開始前的數據庫的快照。 所以 MVCC 可以爲數據庫解決以下問題
- 在併發讀寫數據庫時,可以做到在讀操作時不用阻塞寫操作,寫操作也不用阻塞讀操作,提高了數據庫併發讀寫的性能
- 同時還可以解決髒讀,幻讀,不可重複讀等事務隔離問題,但不能解決更新丟失問題
小結一下咯
簡而言之,MVCC 就是因爲大佬們,不滿意只讓數據庫採用悲觀鎖這樣性能不佳的形式去解決讀-寫衝突問題,而提出的解決方案,所以在數據庫中,因爲有了 MVCC,所以我們可以形成兩個組合:
MVCC + 悲觀鎖
MVCC解決讀寫衝突,悲觀鎖解決寫寫衝突MVCC + 樂觀鎖
MVCC 解決讀寫衝突,樂觀鎖解決寫寫衝突
MVCC 的實現原理
爲了解決**讀寫衝突
,它的實現原理主要是依賴記錄中的** 3個隱式字段
,undo日誌
,Read View
來實現的
隱式字段
1、DB_TRX_ID
6 byte, 最近修改(修改和插入)的事務ID: 創建這條記錄/修改這條記錄的最後一次事務ID
2、DB*_ROLL_PTR*
7 byte, 回滾指針, 指向這條記錄的上一個版本 (指向的是undo log)
3、DB_ROW_ID
6 byte, 隱含的自增 ID(隱藏主鍵),如果數據表沒有主鍵,InnoDB 會自動以DB_ROW_ID 產生一個聚簇索引
實際還有一個刪除 flag 隱藏字段, 既記錄被更新或刪除並不代表真的刪除,而是刪除 flag 變了
undo log
undo log 主要分爲兩種:
1、insert undo log
代表事務在 insert 新記錄時產生的 undo log, 只在事務回滾時需要,並且在事務提交後可以被立即丟棄
2、update undo log
事務在進行 update 或 delete 時產生的 undo log ; 不僅在事務回滾時需要,在快照讀時也需要;所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日誌時,對應的日誌纔會被 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 可見,那麼這條記錄一定是可以被安全清除的。
Read View 讀視圖
說白了 Read View 就是事務進行 快照讀 操作的時候生產的 讀視圖 (Read View),在該事務執行的快照讀的那一刻,會生成數據庫系統當前的一個快照,記錄並維護系統當前活躍事務的 ID (當每個事務開啓時,都會被分配一個 ID , 這個 ID 是遞增的,所以最新的事務,ID 值越大)
Read View 遵循一個可見性算法,主要是將 要被修改的數據 的最新記錄中的 DB_TRX_ID(即當前事務 ID )取出來,與系統當前其他活躍事務的 ID 去對比(由 Read View 維護),如果 DB_TRX_ID 跟 Read View 的屬性做了某些比較,不符合可見性,那就通過 DB_ROLL_PTR 回滾指針去取出 Undo Log 中的 DB_TRX_ID 再比較,即遍歷鏈表的 DB_TRX_ID(從鏈首到鏈尾,即從最近的一次修改查起),直到找到滿足特定條件的 DB_TRX_ID , 那麼這個 DB_TRX_ID 所在的舊記錄就是當前事務能看見的最新老版本
MVCC 相關問題
RC , RR 級別下的 InnoDB 快照讀有什麼不同?
RC: 讀已提交 RR: 可重複讀
正是**
Read View
** 生成時機的不同,從而造成 RC , RR 級別下快照讀的結果的不同
- 在 RR 級別下的某個事務的對某條記錄的第一次快照讀會創建一個快照及 Read View, 將當前系統活躍的其他事務記錄起來,此後在調用快照讀的時候,還是使用的是同一個 Read View,所以只要當前事務在其他事務提交更新之前使用過快照讀,那麼之後的快照讀使用的都是同一個 Read View,所以對之後的修改不可見;
- 即 RR 級別下,快照讀生成 Read View 時,Read View 會記錄此時所有其他活動事務的快照,這些事務的修改對於當前事務都是不可見的。而早於Read View創建的事務所做的修改均是可見
- 而在 RC 級別下的,事務中,每次快照讀都會新生成一個快照和 Read View , 這就是我們在 RC 級別下的事務中可以看到別的事務提交的更新的原因
- 總之在 RC 隔離級別下,是每個快照讀都會生成並獲取最新的 Read View;而在 RR 隔離級別下,則是同一個事務中的第一個快照讀纔會創建 Read View, 之後的快照讀獲取的都是同一個 Read View。
參考資料:
https://blog.csdn.net/bbj12345678/article/details/120780051
【【MySql】理解Read View(事務 undolog MVCC)】 https://www.bilibili.com/video/BV1A24y187ss/?share_source=copy_web&vd_source=95687cb0a9238b1e583346db070aefae