快照讀(SnapShot Read) 是一種一致性不加鎖的讀,是 InnoDB 併發如此之高的核心緣由之一。數據庫
在 READ COMMITTED 事務隔離級別下,一致性不加鎖的讀是指,老是讀取被鎖定行的最新一份快照數據,所以其它事務修改了該行數據,該事務也能讀取到,這也貼合了 RC 隔離級別下容許不可重複讀的問題;segmentfault
在 REPEATABLE READ 事務隔離級別下,一致性不加鎖的讀是指,事務讀取到的數據,要麼是事務開始前就已經存在的數據,要麼是事務自身插入或者修改過的數據。(下面將以此隔離級別說明);併發
不加鎖的簡單的 SELECT 都屬於快照讀,例如:mvc
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,是經過保存數據的歷史版本,經過對數據行的多個版本管理來實現數據庫的併發控制。這樣咱們就能夠經過比較版本號決定數據是否顯示出來,讀取數據的時候不須要加鎖也能夠保證事務的隔離效果(能夠理解成樂觀鎖)。code
多版本併發控制(MVCC)只在可重複讀(REPEATABLE READ)和提交讀(READ COMMITTED)兩個隔離級別下工做,其餘兩個隔離級別都和 MVCC 不兼容,由於未提交讀(READ UNCOMMITTED),老是讀取最新的數據行,而不是符合當前事務版本的數據行;而可串行化(SERIALIZABLE) 則會對全部讀取的行都加鎖。blog
MySQL 的大多數事務型存儲引擎實現的都不是簡單的行級鎖。基於提高併發性能的考慮,它們通常都同時實現了多版本併發控制(MVCC)。不只是 MySQL,包括 Oracle、PostgreSQL 等其餘數據庫系統也都實現了 MVCC,但各自的實現機制不盡相同,由於 MVCC 沒有一個統一的實現標準,典型的有樂觀(optimistic)併發控制和悲觀(pessimistic)併發控制。索引
經過 MVCC 可讓讀寫互相不阻塞,即讀不阻塞寫,寫不阻塞讀,這樣就能夠提高事務併發處理能力。
提升併發的演進思路:
- 普通鎖,只能串行執行;
- 讀寫鎖,能夠實現讀讀併發;
- 數據多版本併發控制,能夠實現讀寫併發。
由於 InnoDB 的 MVCC 採用了樂觀鎖的方式,讀取數據時並不須要加鎖,對於寫操做,也只鎖定必要的行。
一致性非鎖定讀也被稱爲快照讀,這也是 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)