实现mvcc_MySQL 的多版本并发控制(MVCC) 是干啥的?
點擊藍色“架構文摘”關注我喲
加個“星標”,每天上午 09:25,干貨推送!
來源:https://segmentfault.com/a/1190000037557620
作者:白菜1031
一、什么是多版本并發控制
多版本并發控制 技術的英文全稱是 Multiversion Concurrency Control ,簡稱 MVCC 。
多版本并發控制(MVCC)
是通過保存數據在某個時間點的快照來實現并發控制的。也就是說,不管事務執行多長時間,事務內部看到的數據是不受其它事務影響的,根據事務開始的時間不同,每個事務對同一張表,同一時刻看到的數據可能是不一樣的。
簡單來說, 多版本并發控制
的思想就是保存數據的歷史版本,通過對數據行的多個版本管理來實現數據庫的并發控制。這樣我們就可以通過比較版本號決定數據是否顯示出來,讀取數據的時候不需要加鎖也可以保證事務的隔離效果。
可以認為 多版本并發控制(MVCC)
是行級鎖的一個變種,但是它在很多情況下避免了加鎖操作,因此開銷更低。雖然實現機制有所不同,但大都實現了非阻塞的讀操作,寫操作也只鎖定必要的行。
MySQL的大多數事務型存儲引擎實現的都不是簡單的行級鎖?;谔嵘l性能的考慮,它們一般都同時實現了多版本并發控制(MVCC)。不僅是MySQL,包括Oracle、PostgreSQL等其他數據庫系統也都實現了MVCC,但各自的實現機制不盡相同,因為MVCC沒有一個統一的實現標準,典型的有樂觀(optimistic)并發控制 和 悲觀(pessimistic)并發控制 。
二、多版本并發控制解決了哪些問題
1. 讀寫之間阻塞的問題
通過 MVCC 可以讓讀寫互相不阻塞,即讀不阻塞寫,寫不阻塞讀,這樣就可以提升事務并發處理能力。
提高并發的演進思路:
普通鎖,只能串行執行;
讀寫鎖,可以實現讀讀并發;
數據多版本并發控制,可以實現讀寫并發。
2. 降低了死鎖的概率
因為 InnoDB 的 MVCC 采用了樂觀鎖的方式,讀取數據時并不需要加鎖,對于寫操作,也只鎖定必要的行。
3. 解決一致性讀的問題
一致性讀也被稱為 快照讀
,當我們查詢數據庫在某個時間點的快照時,只能看到這個時間點之前事務提交更新的結果,而不能看到這個時間點之后事務提交的更新結果。
三、快照讀與當前讀
快照讀(SnapShot Read) 是一種 一致性不加鎖的讀 ,是 InnoDB并發如此之高的核心原因之一 。
這里的 一致性 是指,事務讀取到的數據,要么是 事務開始前就已經存在的數據 ,要么是 事務自身插入或者修改過的數據 。
不加鎖的簡單的 SELECT 都屬于 快照讀 ,例如:
????`SELECT?*?FROM?t?WHERE?id=1`與 快照讀 相對應的則是 當前讀 , 當前讀 就是讀取最新數據,而不是歷史版本的數據。加鎖的 SELECT 就屬于當前讀,例如:
????SELECT?*?FROM?t?WHERE?id=1?LOCK?IN?SHARE?MODE;????SELECT?*?FROM?t?WHERE?id=1?FOR?UPDATE;
四、InnoDB 的 MVCC 是如何工作的
1. InnoDB 是如何存儲記錄的多個版本的
事務版本號
每開啟一個事務,我們都會從數據庫中獲得一個事務 ID(也就是事務版本號),這個事務 ID 是自增長的,通過 ID 大小,我們就可以判斷事務的時間順序。
行記錄的隱藏列
InnoDB 的葉子段存儲了數據頁,數據頁中保存了行記錄,而在行記錄中有一些重要的隱藏字段:
DB_ROW_ID :6-byte,隱藏的行 ID,用來生成默認聚簇索引。如果我們創建數據表的時候沒有指定聚簇索引,這時 InnoDB 就會用這個隱藏 ID 來創建聚集索引。采用聚簇索引的方式可以提升數據的查找效率。
DB_TRX_ID :6-byte,操作這個數據的事務 ID,也就是最后一個對該數據進行插入或更新的事務 ID。
DB_ROLL_PTR :7-byte,回滾指針,也就是指向這個記錄的 Undo Log 信息。
Undo Log
InnoDB 將行記錄快照保存在了 Undo Log 里,我們可以在回滾段中找到它們,如下圖所示:
Undo Log回滾歷史記錄從圖中能看到回滾指針將數據行的所有快照記錄都通過鏈表的結構串聯了起來,每個快照的記錄都保存了當時的 db_trx_id,也是那個時間點操作這個數據的事務
ID。這樣如果我們想要找歷史快照,就可以通過遍歷回滾指針的方式進行查找。
2. 在 可重復讀(REPEATABLE READ) 隔離級別下, InnoDB 的 MVCC 是如何工作的
查詢(SELECT)
InnoDB 會根據以下兩個條件檢查每行記錄:
InnoDB只查找版本早于當前事務版本的數據行(也就是,行的系統版本號小于或等于事務的系統版本號),這樣可以 確保事務讀取的行,要么是在事務開始前已經存在的,要么是事務自身插入或者修改過的 。
行的刪除版本要么未定義,要么大于當前事務版本號。這可以確保 事務讀取到的行,在事務開始之前未被刪除 。
只有符合上述兩個條件的記錄,才能返回作為查詢結果。
插入(INSERT)
InnoDB為新插入的每一行保存當前系統版本號作為行版本號。
刪除(DELETE)
InnoDB為刪除的每一行保存當前系統版本號作為行刪除標識。?
刪除在內部被視為更新,行中的一個特殊位會被設置為已刪除。
更新(UPDATE)
InnoDB為插入一行新記錄,保存當前系統版本號作為行版本號,同時保存當前系統版本號到原來的行作為行刪除標識。
五、總結
多版本并發控制(MVCC) 在一定程度上實現了 讀寫并發 ,它只在 可重復讀(REPEATABLE READ) 和提交讀(READ COMMITTED) 兩個隔離級別下工作。其他兩個隔離級別都和 MVCC 不兼容,因為 未提交讀(READ UNCOMMITTED) ,總是讀取最新的數據行,而不是符合當前事務版本的數據行。而 可串行化(SERIALIZABLE)
則會對所有讀取的行都加鎖。
行鎖,并發,事務回滾 等多種特性都和MVCC相關。
參考
MySQL5.7文檔:innodb-multi-versioning ?
《高性能MySQL》
end
推薦閱讀:
TCP 三次握手、四手揮手,這樣說你能明白吧!
拜托,不要再問我線程池啦!
為什么 Redis 單線程還這么快?
Spring Cloud架構的各個組件的原理分析
一口氣說出 5 種 IO 模型,蒙圈了!
如有收獲,點個在看,誠摯感謝
總結
以上是生活随笔為你收集整理的实现mvcc_MySQL 的多版本并发控制(MVCC) 是干啥的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java没有这样的元素异常_java
- 下一篇: mysql_unbuffered_que