背景
- MySql 的大多數事務型存儲引擎實現的其實都不是簡單的行級鎖。基於提升並發性能的考慮,它們一般都實現了多版本並發控制(MVCC)。
- 可以認為 MVCC 是行級鎖的一個變種,但是它在很多情況下避免了加鎖操作,因此開銷更低。雖然實現機制有所不同,但大都實現了非阻塞的讀操作,寫操作也只是鎖定必要的行。
- MVCC 的實現方式有很多中,典型的有樂觀並發控制和悲觀並發控制。
- MVCC 只在 READ COMMITTED 讀已提交 和 REPEATABLE READ 可重復讀兩個隔離級別下工作。其他兩個隔離級別和 MVCC 不兼容,因為 READ UNCOMMITED 讀未提交總是讀取最新的數據行,而不是符合當前事務版本的數據行。而 SERIALIZABLE 串行化會對所有讀取的行都加鎖。
總結
- MVCC 是被 MySql 中
事務型存儲引擎 InnoDB
所支持的。 - 應對高並發事務,MVCC 比單純的加行鎖更有效,開銷更小。
- MVCC 只在
READ COMMIT
和REPEATABLE READ
兩個隔離級別下工作。 - MVCC 可以使用樂觀和悲觀來實現。
分析
-
InnoDB 存儲引擎在數據庫沒行數據后面添加了三個字段。
- 6個字節的事務 ID(DB_TRX_ID)字段:標記了最新更新這條行記錄的 transaction id,每處理一個事務,其值自動 +1。
- 7個字節的回滾指針(DB_ROLL_PTR)字段:指向當前記錄的 rollback segment 的 undo log(撤銷日志記錄),找之前版本的數據就是通過這個指針。
- 6個字節的 DB_ROW_ID 字段:當由 innodb 自動產生聚集索引時,聚集索引包括這個 DB_ROW_ID 的值,否則聚集索引中不包括這個值,這個用於索引當中。
-
下面來演示一下事務對行記錄的更新過程。
!
-
read view
在 innodb 中,每創建一個新事物,存儲引擎都會將當前系統中的活躍事務列表創建一個副本(
read view
),副本中保存的是系統中當前不應該被本事務看到的其他事務 id 列表。當用戶在事務中要讀取某行記錄的時候,innodb 會將該行的當前版本好與 read view 進行比較,比較時通過比較算法。
比較算法
假設該行當前的事務 id 為 trx_id_current,read_view 中該行最早的事務 id 為 trx_id_first,最遲的事務 id 為 tax_id_last。
-
如果 trx_id_current < trx_id_first,那就表示
當前事務在讀取該行記錄時,事務 ID 比其他的都要小。那就意味着,當前所有和該行記錄有關的事務中,當前事務是第一個讀取到該行記錄的,沒有任何在當前事務前面對該行數據做過更改但還沒有提交的事務,所以當前事務可以直接拿到表中的穩定的數據!
-
如果 trx_id_current > tax_id_last,那就表示
當前事務在讀取該行記錄時,事務 ID 比其他的 read_view 的事務 ID 都要大。那就意味着,當前所有和該行記錄有關的事務中,當前事務是最后一個讀取到該行記錄的。需要根據該行記錄的 DB_ROLL_PTR 指針所指向的回滾段中取出最新的 undo-log 版本號,然后繼續迭代下去,在 undo-log 中一層層往下找,最終就會找到穩定的數據!
-
trx_id_first < trx_id_current < tax_id_last,同上。
對比READ COMMITED
和REPEATABLE READ
READ COMMITED
讀已提交,只能解決臟讀問題,但是不能解決不可重復讀問題,所以每次都能讀取到最新一份數據快照。
REPEATABLE READ
可重復讀,解決了臟讀和不可重復讀的問題,所以能讀到事務開始前時的數據版本。
- 使得
READ COMMITED
級別能夠保證, 只要是當前語句執行前已經提交的數據都是可見的。 - 使得
REPEATABLE READ
級別能夠保證, 只要是當前事務執行前已經提交的數據都是可見的。
總結
- 一般我們認為MVCC有下面幾個特點:
- 每行數據都存在一個版本,每次數據更新時都更新該版本
- 修改時Copy出當前版本, 然后隨意修改,各個事務之間無干擾
- 保存時比較版本號,如果成功(commit),則覆蓋原記錄, 失敗則放棄copy(rollback)
- 就是每行都有版本號,保存時根據版本號決定是否成功,聽起來含有樂觀鎖的味道, 因為這看起來正是,在提交的時候才能知道到底能否提交成功
- 而InnoDB實現MVCC的方式是:
- 事務以排他鎖的形式修改原始數據
- 把修改前的數據存放於undo log,通過回滾指針與主數據關聯
- 修改成功(commit)啥都不做,失敗則恢復undo log中的數據(rollback)
- 二者本質的區別是 InnoDB 使用了 拍他鎖。
- InnoDB 的實現算不上 MVCC,因為沒有實現核心的多版本共存,undo log 中的內容只是串行化的結果,記錄了多個事務的過程,不屬於多版本共存。
- 因為InnoDB使用的MVCC中結合了排他鎖, 不是純的MVCC, 所以第一類更新丟失是不會出現了, 一般說更新丟失都是指第二類丟失更新。