MySQL的多版本併發控制(MVCC).

一、快照讀與當前讀

快照讀(SnapShot Read) 是一種一致性不加鎖的讀,是 InnoDB 併發如此之高的核心原因之一。

在 READ COMMITTED 事務隔離級別下,一致性不加鎖的讀是指,總是讀取被鎖定行的最新一份快照數據,因此其它事務修改了該行數據,該事務也能讀取到,這也貼合了 RC 隔離級別下存在幻讀的問題;

在 REPEATABLE READ 事務隔離級別下,一致性不加鎖的讀是指,事務讀取到的數據,要麼是事務開始前就已經存在的數據,要麼是事務自身插入或者修改過的數據。(下面將以此隔離級別說明);

不加鎖的簡單的 SELECT 都屬於快照讀,例如:

SELECT * FROM t WHERE id=1;

與快照讀相對應的則是當前讀(Current Read),當前讀就是讀取最新數據,而不是歷史版本的數據。加鎖的 SELECT 就屬於當前讀,例如:

SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE;
SELECT * FROM t WHERE id=1 FOR UPDATE;

SELECT...FOR UPDATE 對讀取的行記錄加一個 X 鎖,其它事務不能對已鎖定的行加上任何鎖。

SELECT...LOCK IN SHARE MODE 對讀取的行記錄加一個 S 鎖,其它事務可以向被鎖定的行加 S 鎖,但是如果加 X 鎖,則會被阻塞。

二、基於快照讀的多版本併發控制

多版本併發控制技術的英文全稱是:Multiversion Concurrency Control,簡稱 MVCC,是通過保存數據的歷史版本,通過對數據行的多個版本管理來實現數據庫的併發控制。這樣我們就可以通過比較版本號決定數據是否顯示出來,讀取數據的時候不需要加鎖也可以保證事務的隔離效果(可以理解成樂觀鎖)。

多版本併發控制(MVCC)只在可重複讀(REPEATABLE READ)和提交讀(READ COMMITTED)兩個隔離級別下工作,其他兩個隔離級別都和 MVCC 不兼容,因爲未提交讀(READ UNCOMMITTED),總是讀取最新的數據行,而不是符合當前事務版本的數據行;而可串行化(SERIALIZABLE) 則會對所有讀取的行都加鎖。

MySQL 的大多數事務型存儲引擎實現的都不是簡單的行級鎖。基於提升併發性能的考慮,它們一般都同時實現了多版本併發控制(MVCC)。不僅是 MySQL,包括 Oracle、PostgreSQL 等其他數據庫系統也都實現了 MVCC,但各自的實現機制不盡相同,因爲 MVCC 沒有一個統一的實現標準,典型的有樂觀(optimistic)併發控制和悲觀(pessimistic)併發控制。

三、多版本併發控制解決了哪些問題?

1. 讀寫之間阻塞的問題

通過 MVCC 可以讓讀寫互相不阻塞,即讀不阻塞寫,寫不阻塞讀,這樣就可以提升事務併發處理能力。

提高併發的演進思路:

  • 普通鎖,只能串行執行;
  • 讀寫鎖,可以實現讀讀併發;
  • 數據多版本併發控制,可以實現讀寫併發。

2. 降低了死鎖的概率

因爲 InnoDB 的 MVCC 採用了樂觀鎖的方式,讀取數據時並不需要加鎖,對於寫操作,也只鎖定必要的行。

3. 解決一致性讀的問題

一致性讀也被稱爲快照讀,當我們查詢數據庫在某個時間點的快照時,只能看到這個時間點之前事務提交更新的結果,而不能看到這個時間點之後事務提交的更新結果。

四、InnoDB 的 MVCC 是如何工作的?

1. InnoDB 是如何存儲記錄的多個版本的?

事務版本號: 每開啓一個事務,我們都會從數據庫中獲得一個事務 ID(也就是事務版本號),這個事務 ID 是自增長的,通過 ID 大小,我們就可以判斷事務的時間順序。

行記錄的隱藏列: InnoDB 的葉子段存儲了數據頁,數據頁中保存了行記錄,而在行記錄中有一些重要的隱藏字段:

  • DB_ROW_ID:6-byte,隱藏的行 ID,用來生成默認聚簇索引。如果我們創建數據表的時候沒有指定聚簇索引,這時 InnoDB 就會用這個隱藏 ID 來創建聚集索引。採用聚簇索引的方式可以提升數據的查找效率。
  • DB_TRX_ID:6-byte,操作這個數據的事務 ID,也就是最後一個對該數據進行插入或更新的事務 ID。(InnoDB 的插入、更新、刪除都會更新該事務 ID,同時刪除會將一個特殊位標記爲已刪除)
  • DB_ROLL_PTR:7-byte,回滾指針,也就是指向這個記錄的 Undo Log 信息。

Undo Log: InnoDB 將行記錄快照保存在了 Undo Log 裏,我們可以在回滾段中找到它們,如下圖所示,回滾指針將數據行的所有快照記錄都通過鏈表的結構串聯了起來,每個快照的記錄都保存了當時的 db_trx_id,也是那個時間點操作這個數據的事務 ID。這樣如果我們想要找歷史快照,就可以通過遍歷回滾指針的方式進行查找。



參考鏈接:MySQL的多版本併發控制(MVCC)

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