共計 2575 個字符,預計需要花費 7 分鐘才能閱讀完成。
本篇內容主要講解“MySQL 多版本并發控制機制源碼分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“MySQL 多版本并發控制機制源碼分析”吧!
MVCC(多版本并發控制機制)
隔離性也可以被稱作并發控制、可串行化等。談到并發控制首先想到的就是鎖,MySQL 通過使用兩階段鎖的方式實現了更新的可串行化,同時為了加速查詢性能,采用了 MVCC(Multi Version Concurrency Control)的機制,使得不用鎖也可以獲取一致性的版本。
Repeatable Read
MySQL 的通過 MVCC 以及 (Next-Key Lock) 實現了可重復讀 (Repeatable Read), 其思想(MVCC) 就是記錄數據的版本變遷,通過精巧的選擇不同數據的版本從而能夠對用戶呈現一致的結果。如下圖所示:
上圖中,(A=50|B=50)的初始版本為 1。
1. 事務 t1 在 select A 時候看到的版本為 1,即 A =50
2. 事務 t2 對 A 和 B 的修改將版本升級為 2, 即 A =0,B=100
3. 事務 t1 再此 select B 的時候看到的版本還是 1, 即 B =50
這樣就隔離了版本的影響,A+ B 始終為 100。
Read Commit
而如果不通過版本控制機制,而是讀到最近提交的結果的話,則隔離級別是 read commit, 如下圖所示:
在這種情況下,就需要使用鎖機制 (例如 select for update) 將此 A,B 記錄鎖住,從而獲得正確的一致結果, 如下圖所示:
MVCC 的優勢
當我們要對一些數據做一些只讀操作來檢查一致性,例如檢查賬務是否對齊的操作時候,并不希望加上對性能損耗很大的鎖。這時候 MVCC 的一致性版本就有很大的優勢了。
MVCC(實現機制)
本節就開始談談 MVCC 的實現機制, 注意 MVCC 僅僅在純 select 時有效(不包括 select for update,lock in share mode 等加鎖操作, 以及 updateinsert 等)。
select 運行棧
首先我們追蹤一下一條普通的查詢 sql 在 mysql 源碼中的運行過程,sql 為(select * from test);
其運行棧為:
由于 mysql 默認隔離級別是 repeatable_read(RR), 所以 read_record 重載為 rr_sequential(當前我們并不關心 select 通過 index 掃描出 row 之后再通過 condition 過濾的過程)。繼續追蹤:
讓我們看下該函數內部:
bool lock_clust_rec_cons_read_sees(const rec_t* rec /* 由 innodb 掃描出來的一行 */,....){ ... // 從當前掃描的行中獲取其 *** 修改的版本 trx_id(事務 id) trx_id = row_get_rec_trx_id(rec, index, offsets); // 通過參數 (一致性快照視圖和事務 id) 決定看到的行快照 return(read_view_sees_trx_id(view, trx_id)); }
read_view 的創建過程
我們先關注一致性視圖的創建過程, 我們先看下 read_view 結構:
然后通過 debug,發現創建 read_view 結構也是在上述的 rr_sequential 中操作的,繼續跟蹤調用棧:
我們看下 row_search_for_mysql 里的一個分支:
row_search_for_mysql: // 這邊只有 select 不加鎖模式的時候才會創建一致性視圖 else if (prebuilt- select_lock_type == LOCK_NONE) { // 創建一致性視圖 trx_assign_read_view(trx); prebuilt- sql_stat_start = FALSE; }
上面的注釋就是 select for update(in share model)不會走 MVCC 的原因。讓我們進一步分析 trx_assign_read_view 函數:
好了,終于到了創建 read_view 的主要階段, 主要過程如下圖所示:
代碼過程為:
行版本可見性:
由上面的 lock_clust_rec_cons_read_sees 可知, 行版本可見性由 read_view_sees_trx_id 函數判斷:
其實上述函數就是一個二分法,read_view 其實保存的是當前活躍事務的所有事務 id, 如果當前行版本對應修改的事務 id 不在當前活躍事務里面的話,就返回 true, 表示當前版本可見,否則就是不可見, 如下圖所示。
接上述 lock_clust_rec_cons_read_sees 的返回:
undolog 搜索可見版本的過程
我們現在考察一下 row_sel_build_prev_vers_for_mysql 函數:
row_sel_build_prev_vers_for_mysql |-row_vers_build_for_consistent_read
主要是調用了 row_ver_build_for_consistent_read 方法返回可見版本:
整個過程如下圖所示:
至于 undolog 怎么恢復出對應版本的 row 記錄就又是一個復雜的過程了,由于篇幅原因,在此略過不表。
read_view 創建時機再討論
在創建一致性視圖的 row_search_for_mysql 的代碼中
trx_assign_read_view 中由這么一段代碼
所以綜合這兩段代碼,即在一個事務中,只有 *** 次運行 select(不加鎖)的時候才會創建一致性視圖, 如下圖所示:
筆者構造了此種場景模擬過,確實如此。
MVCC 和鎖的同時作用導致的一些現象
MySQL 是通過 MVCC 和二階段鎖 (2PL) 來兼顧性能和一致性的,但是由于 MySQL 僅僅在 select 時候才創建一致性視圖,而在 update 等加鎖操作的時候并不做如此操作,所以就會產生一些詭異的現象。如下圖所示:
如果理解了 update 不走一致性視圖(read_view),而 select 走一致性視圖(read_view),就可以很好解釋這個現象。如下圖所示:
到此,相信大家對“MySQL 多版本并發控制機制源碼分析”有了更深的了解,不妨來實際操作一番吧!這里是丸趣 TV 網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!