MySQL的多版本并發(fā)控制MVCC的實現(xiàn)
什么是MVCC
MVCC就是多版本并發(fā)控制。
MySQL的事務(wù)型存儲引擎通過多版本并發(fā)控制(MVCC)來提升并發(fā)性能。
可以認為MVCC是行級鎖的一個變種,但是它在大多數(shù)情況下避免了加鎖操作,同時實現(xiàn)非阻塞的讀操作,因此開銷更低。
MVCC是通過保存數(shù)據(jù)在某個時間點的快照來實現(xiàn)的,核心思想就是保存數(shù)據(jù)的歷史版本,通過對數(shù)據(jù)行的多個版本管理來實現(xiàn)數(shù)據(jù)庫的并發(fā)控制。
這樣我們就可以通過比較版本號決定數(shù)據(jù)是否顯示出來,讀取數(shù)據(jù)的時候不需要加鎖也可以保證事務(wù)的隔離效果。
MVCC的實現(xiàn)
實際上,InnoDB 會在每行記錄后面增加三個隱藏字段:
- ROW_ID:行ID,隨著插入新行而單調(diào)遞增,如果有主鍵,則不會包含該列。
- TRX_ID:記錄插入或更新該行的事務(wù)的事務(wù)ID。
- ROLL_PTR:回滾指針,指向 undo log 記錄。每次對某條記錄進行改動時,該列會存一個指針,可以通過這個指針找到該記錄修改前的信息。當某條記錄被多次修改時,該行記錄會存在多個版本,通過 ROLL_PTR 鏈接形成一個類似版本鏈的概念。

以 RR 級別為例:
每開啟一個事務(wù)時,系統(tǒng)會給該事務(wù)分配一個事務(wù) Id,在該事務(wù)執(zhí)行第一 個 select 語句的時候,會生成一個當前時間點的事務(wù)快照 ReadView,主要包含以下幾個屬性:
- m_ids:表示生成ReadView時,當前系統(tǒng)中未提交的讀寫事務(wù)的事務(wù)id列表。
- min_trx_id:表示生成ReadView時,當前系統(tǒng)中未提交的讀寫事務(wù)中最小的事務(wù)id,也就是m_ids中的最小值。
- max_trx_id:表示生成ReadView時,系統(tǒng)中應(yīng)該分配給下一個事務(wù)的id值。
- creator_trx_id:表示生成ReadView時,該事務(wù)的事務(wù)id。
有了這個 ReadView,這樣在訪問某條記錄時,只需要按照下邊的步驟判斷記錄的某個版本是否可見:
- trx_id == creator_trx_id:可以訪問這個版本。
- trx_id < min_trx_id :可以訪問這個版本。
- trx_id > max_trx_id:不可以訪問這個版本。
- min_trx_id <= trx_id <= max_trx_id :如果trx_id是在m_ids中,不可以訪問這個版本,反之可用。
在進行判斷時,首先會拿記錄的最新版本來比較,如果該版本無法被當前事務(wù)看到,則通過記錄的 ROLL_PTR 找到上一個版本,重新進行比較,直到找到一個能被當前事務(wù)看到的版本。
而對于刪除,其實就是一種特殊的更新,InnoDB 用一個額外的標記位 delete_bit 標識是否刪除。當我們在進行判斷時,會檢查下 delete_bit 是否被標記,如果是,則跳過該版本,通過 ROLL_PTR 拿到下一個版本進行判斷。
以上內(nèi)容是對于 RR 級別來說,而對于 RC 級別,其實整個過程幾乎一樣,唯一不同的是生成 ReadView 的時機, RR 級別只在事務(wù)開始時生成一次,之后一直使用該 ReadView。而 RC 級別則在每次 select 時,都會生成一個 ReadView。
MVCC 有沒有解決幻讀?
幻讀:在一個事務(wù)中使用相同的 SQL 進行兩次讀取,第二次讀取到了其他事務(wù)新插入的行。
例如:
1)事務(wù) 1 第一次查詢:select * from user where id < 10 時查到了 id = 1 的數(shù)據(jù)
2)事務(wù) 2 插入了 id = 2 的數(shù)據(jù)
3)事務(wù) 1 使用同樣的語句第二次查詢時,查到了 id = 1、id = 2 的數(shù)據(jù),出現(xiàn)了幻讀。
談到幻讀,首先我們要引入“當前讀”和“快照讀”的概念。
- 快照讀:生成一個事務(wù)快照(ReadView),之后都從這個快照獲取數(shù)據(jù)。普通 select 語句就是快照讀。
- 當前讀:讀取數(shù)據(jù)的最新版本。常見的 update/insert/delete、還有 select ... for update、select ... lock in share mode 都是當前讀。
對于快照讀,MVCC 因為從 ReadView 讀取,所以必然不會看到新插入的行,所以天然就解決了幻讀的問題。
而對于當前讀的幻讀,MVCC 是無法解決的。需要使用 Gap Lock 或 Next-Key Lock(Gap Lock + Record Lock)來解決。
其實原理也很簡單,用上面的例子稍微修改下以觸發(fā)當前讀:
select * from user where id < 10 for update
當使用了 Gap Lock 時,Gap 鎖會鎖住 id < 10 的整個范圍,因此其他事務(wù)無法插入 id < 10 的數(shù)據(jù),從而防止了幻讀。
到此這篇關(guān)于MySQL的多版本并發(fā)控制MVCC的實現(xiàn)的文章就介紹到這了,更多相關(guān)MySQL多版本并發(fā)控制MVCC內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MySQL?MVCC(多版本并發(fā)控制)實現(xiàn)機制詳細講解
- MySQL多版本并發(fā)控制mvcc原理淺析
- 一文詳解MYSQL的多版本并發(fā)控制MVCC(Multi-Version Concurrency Control)
- Mysql InnoDB多版本并發(fā)控制MVCC詳解
- MySQL多版本并發(fā)控制MVCC詳解
- Mysql MVCC多版本并發(fā)控制詳情
- MySQL多版本并發(fā)控制MVCC底層原理解析
- MySQL多版本并發(fā)控制MVCC深入學(xué)習(xí)
- mysql多版本并發(fā)控制MVCC的實現(xiàn)
- MySQL中MVCC多版本并發(fā)控制
相關(guān)文章
MySQL數(shù)據(jù)庫中存儲圖片和讀取圖片的操作代碼
在MySQL數(shù)據(jù)庫中存儲圖片通常有兩種主要方式:將圖片以二進制數(shù)據(jù)(BLOB 類型)直接存儲在數(shù)據(jù)庫中,或者將圖片文件存儲在服務(wù)器文件系統(tǒng)上,而在數(shù)據(jù)庫中存儲圖片的路徑或URL,以下是這兩種方法的詳細解釋,包括存儲和讀取操作,需要的朋友可以參考下2024-11-11
Mysql LONGBLOB 類型存儲二進制數(shù)據(jù) (修改+調(diào)試+整理)
Mysql用戶創(chuàng)建以及權(quán)限賦予操作的實現(xiàn)
Can''t connect to MySQL server on ''localhost'' (10048)問題解決方
mysql的expire_logs_days參數(shù)引發(fā)的問題及解決

