MySQL?MVCC(多版本并發(fā)控制)實(shí)現(xiàn)機(jī)制詳細(xì)講解
前言
MVCC(Multi-Version Concurrency Control,多版本并發(fā)控制)是InnoDB存儲(chǔ)引擎解決讀寫沖突、提升并發(fā)性能的核心技術(shù)——它通過為數(shù)據(jù)行維護(hù)多個(gè)版本(快照),讓讀操作(SELECT)無需加鎖即可讀取數(shù)據(jù),實(shí)現(xiàn)讀不阻塞寫、寫不阻塞讀的高并發(fā)效果。
一、MVCC核心概念
1.1 什么是MVCC
MVCC是InnoDB專為讀已提交(RC) 和可重復(fù)讀(RR) 隔離級(jí)別設(shè)計(jì)的并發(fā)控制機(jī)制,核心特征如下:
- 核心思想:為每一行數(shù)據(jù)維護(hù)多個(gè)版本(快照),不同事務(wù)讀取時(shí),根據(jù)規(guī)則選擇對(duì)應(yīng)版本的數(shù)據(jù),而非直接讀取最新數(shù)據(jù);
- 適用場景:僅針對(duì)快照讀(普通
SELECT) 生效,當(dāng)前讀(SELECT ```FOR UPDATE/LOCK IN SHARE MODE、INSERT/UPDATE/DELETE)仍需加鎖; - 目標(biāo):解決“讀寫沖突”,避免傳統(tǒng)鎖機(jī)制中“讀阻塞寫、寫阻塞讀”的問題,提升并發(fā)性能。
1.2 MVCC的核心目標(biāo)
- 讀操作無鎖化:快照讀無需加行鎖/表鎖,不阻塞寫操作;
- 數(shù)據(jù)一致性:保證不同事務(wù)在各自隔離級(jí)別下,讀取到符合規(guī)則的一致數(shù)據(jù);
- 歷史版本可回溯:通過版本鏈,支持事務(wù)讀取“過去某個(gè)時(shí)間點(diǎn)”的數(shù)據(jù)(如RR隔離級(jí)別的可重復(fù)讀)。
二、MVCC實(shí)現(xiàn)的三大基礎(chǔ)組件
InnoDB的MVCC依賴行記錄隱藏字段、Undo Log(回滾日志)、Read View(讀視圖) 三大核心組件,三者協(xié)同完成多版本數(shù)據(jù)的管理和讀取。
2.1 行記錄的隱藏字段
InnoDB為每一行數(shù)據(jù)(除自定義字段外)自動(dòng)添加3個(gè)隱藏字段,是MVCC的基礎(chǔ):
| 隱藏字段 | 字段類型 | 核心作用 |
|---|---|---|
DB_TRX_ID | 6字節(jié) | 記錄最后一次插入/更新該行數(shù)據(jù)的事務(wù)ID(刪除視為特殊的更新,標(biāo)記刪除); |
DB_ROLL_PTR | 7字節(jié) | 回滾指針,指向該行數(shù)據(jù)的Undo Log版本鏈(通過該指針可回溯歷史版本); |
DB_ROW_ID | 6字節(jié) | 聚簇索引無主鍵/唯一鍵時(shí),InnoDB自動(dòng)生成的行ID(僅用于標(biāo)識(shí)行,非MVCC核心); |
示例:
假設(shè)有表user(id INT PRIMARY KEY, name VARCHAR(20)),插入一行(1, '張三'),該行的實(shí)際存儲(chǔ)結(jié)構(gòu)為:
| id | name | DB_TRX_ID | DB_ROLL_PTR | DB_ROW_ID |
|---|---|---|---|---|
| 1 | 張三 | 100 | 指向Undo Log | NULL |
(注:DB_TRX_ID=100表示插入該記錄的事務(wù)ID為100)
2.2 Undo Log(回滾日志)
Undo Log是InnoDB在修改數(shù)據(jù)時(shí),記錄的“數(shù)據(jù)修改前的快照”,是實(shí)現(xiàn)版本鏈的核心載體。
2.2.1 Undo Log的類型
Insert Undo Log:僅記錄INSERT操作的日志,事務(wù)提交后可直接刪除(因INSERT的記錄僅當(dāng)前事務(wù)可見,無版本回溯需求);Update Undo Log:記錄UPDATE/DELETE操作的日志,事務(wù)提交后需保留(供其他事務(wù)的MVCC讀取歷史版本),直到?jīng)]有事務(wù)需要訪問該版本時(shí),由Purge線程清理。
2.2.2 版本鏈(Version Chain)
每次更新行數(shù)據(jù)時(shí),InnoDB會(huì)按以下步驟維護(hù)版本鏈:
- 將更新前的數(shù)據(jù)寫入
Update Undo Log; - 更新行記錄的
DB_TRX_ID為當(dāng)前事務(wù)ID; - 更新行記錄的
DB_ROLL_PTR,指向剛生成的Update Undo Log; - 多次更新后,
DB_ROLL_PTR會(huì)串聯(lián)所有歷史版本,形成版本鏈(鏈頭是最新版本,鏈尾是最早版本)。
版本鏈?zhǔn)纠?/strong>:
當(dāng)前行版本(DB_TRX_ID=102) → DB_ROLL_PTR → 版本1(DB_TRX_ID=101) → DB_ROLL_PTR → 版本0(DB_TRX_ID=100)
2.3 Read View(讀視圖)
Read View是事務(wù)執(zhí)行快照讀時(shí),生成的一個(gè)“可見性判斷規(guī)則”,核心作用是:判斷當(dāng)前事務(wù)能看到哪些版本的數(shù)據(jù)。
2.3.1 Read View的核心字段
Read View包含4個(gè)關(guān)鍵字段,用于版本可見性判斷:
| 字段名 | 含義 |
|---|---|
m_ids | 生成Read View時(shí),當(dāng)前活躍的事務(wù)ID集合(未提交的事務(wù)ID); |
min_trx_id | m_ids中的最小事務(wù)ID(活躍事務(wù)的最小ID); |
max_trx_id | 系統(tǒng)下一個(gè)要分配的事務(wù)ID(大于當(dāng)前所有已分配的事務(wù)ID); |
creator_trx_id | 創(chuàng)建該Read View的事務(wù)ID(當(dāng)前執(zhí)行快照讀的事務(wù)ID); |
2.3.2 版本可見性判斷規(guī)則
事務(wù)讀取行數(shù)據(jù)時(shí),通過Read View判斷該行的某個(gè)版本是否可見:
假設(shè)待判斷版本的DB_TRX_ID = trx_id,規(guī)則如下:
- 若
trx_id < min_trx_id:該版本由“已提交的事務(wù)”生成,可見; - 若
trx_id >= max_trx_id:該版本由“未來的事務(wù)”生成(當(dāng)前事務(wù)未開始時(shí),該事務(wù)還未創(chuàng)建),不可見; - 若
min_trx_id ≤ trx_id < max_trx_id:- 若
trx_id ∈ m_ids:該版本由“當(dāng)前活躍的未提交事務(wù)”生成,不可見; - 若
trx_id ∉ m_ids:該版本由“已提交的事務(wù)”生成,可見;
- 若
- 若版本不可見,則通過
DB_ROLL_PTR回溯版本鏈,直到找到第一個(gè)可見的版本(或無可見版本)。
三、MVCC核心執(zhí)行邏輯(以RR隔離級(jí)別為例)
以MySQL默認(rèn)的可重復(fù)讀(RR) 隔離級(jí)別為例,拆解MVCC在INSERT/UPDATE/DELETE/SELECT中的執(zhí)行流程。
3.1 數(shù)據(jù)插入(INSERT)
- 事務(wù)T1(ID=100)執(zhí)行
INSERT INTO user(id, name) VALUES (1, '張三'); - InnoDB為該行數(shù)據(jù)寫入
DB_TRX_ID=100,DB_ROLL_PTR=NULL(無歷史版本); - 生成
Insert Undo Log(僅用于事務(wù)回滾,提交后刪除); - 事務(wù)提交后,該行數(shù)據(jù)的版本僅對(duì)已提交的事務(wù)可見。
3.2 數(shù)據(jù)更新(UPDATE)
- 事務(wù)T2(ID=101)執(zhí)行
UPDATE user SET name='李四' WHERE id=1; - InnoDB先將該行當(dāng)前版本(
DB_TRX_ID=100)寫入Update Undo Log; - 更新行記錄的
DB_TRX_ID=101,DB_ROLL_PTR指向剛生成的Update Undo Log; - 此時(shí)版本鏈:當(dāng)前版本(101)→ 歷史版本(100);
- 事務(wù)提交后,
Update Undo Log保留,供其他事務(wù)讀取歷史版本。
3.3 數(shù)據(jù)刪除(DELETE)
DELETE被InnoDB視為“特殊的UPDATE”,執(zhí)行流程如下:
- 事務(wù)T3(ID=102)執(zhí)行
DELETE FROM user WHERE id=1; - InnoDB將該行當(dāng)前版本寫入
Update Undo Log; - 更新行記錄的
DB_TRX_ID=102,并標(biāo)記“刪除標(biāo)識(shí)”(物理刪除由Purge線程異步完成); - 版本鏈新增一條:當(dāng)前版本(102,標(biāo)記刪除)→ 版本1(101)→ 版本0(100)。
3.4 數(shù)據(jù)讀?。⊿ELECT,快照讀)
假設(shè)當(dāng)前有活躍事務(wù)ID:101、102,事務(wù)T4(ID=103)執(zhí)行SELECT * FROM user WHERE id=1(RR隔離級(jí)別):
- T4第一次執(zhí)行SELECT時(shí),生成Read View:
m_ids = {101, 102},min_trx_id=101,max_trx_id=104,creator_trx_id=103;
- 讀取行數(shù)據(jù)的當(dāng)前版本(
DB_TRX_ID=102),判斷可見性:102 ≥ min_trx_id(101)且102 < max_trx_id(104),且102 ∈ m_ids→ 不可見;
- 通過
DB_ROLL_PTR回溯版本鏈,讀取上一個(gè)版本(DB_TRX_ID=101):101 ∈ m_ids→ 不可見;
- 繼續(xù)回溯到版本0(
DB_TRX_ID=100):100 < min_trx_id(101)→ 可見;
- 返回該版本的數(shù)據(jù)(
name='張三'); - RR隔離級(jí)別下,T4后續(xù)的SELECT會(huì)復(fù)用該Read View,因此多次讀取結(jié)果一致(可重復(fù)讀)。
四、不同隔離級(jí)別下的MVCC行為
MVCC僅在讀已提交(RC) 和可重復(fù)讀(RR) 隔離級(jí)別生效,核心區(qū)別是Read View的創(chuàng)建時(shí)機(jī):
| 隔離級(jí)別 | Read View創(chuàng)建時(shí)機(jī) | 讀取結(jié)果特點(diǎn) |
|---|---|---|
| 讀已提交(RC) | 每次執(zhí)行快照讀(SELECT)時(shí),重新創(chuàng)建Read View | 同一事務(wù)內(nèi)多次SELECT可能讀取到不同版本(不可重復(fù)讀),僅能看到已提交的最新版本; |
| 可重復(fù)讀(RR) | 事務(wù)內(nèi)第一次執(zhí)行快照讀時(shí)創(chuàng)建Read View,后續(xù)復(fù)用 | 同一事務(wù)內(nèi)多次SELECT讀取結(jié)果一致(可重復(fù)讀),僅能看到事務(wù)啟動(dòng)時(shí)已提交的版本; |
| 讀未提交(RU) | 不使用MVCC,直接讀取最新數(shù)據(jù)(無版本控制) | 能看到未提交事務(wù)的數(shù)據(jù),存在臟讀; |
| 串行化(SERIALIZABLE) | 禁用MVCC,所有讀操作加表鎖 | 完全串行執(zhí)行,無并發(fā)沖突,但性能極低; |
五、MVCC的優(yōu)勢與局限
5.1 優(yōu)勢
- 高并發(fā):快照讀無鎖,讀寫互不阻塞,大幅提升并發(fā)性能;
- 數(shù)據(jù)一致性:在RC/RR隔離級(jí)別下,保證讀取數(shù)據(jù)的一致性,避免臟讀(RC/RR)、不可重復(fù)讀(RR);
- 無鎖讀:無需加行鎖/表鎖,減少鎖競爭和死鎖概率。
5.2 局限
- 版本鏈開銷:大量更新操作會(huì)導(dǎo)致版本鏈過長,增加讀取時(shí)的回溯成本;
- Undo Log清理:Purge線程需異步清理過期的Undo Log,若清理不及時(shí)會(huì)占用磁盤空間;
- 僅適用于快照讀:當(dāng)前讀(如
SELECT ```FOR UPDATE)仍需加鎖,無法避免寫沖突。
總結(jié)
- 核心依賴:MVCC基于行記錄隱藏字段(
DB_TRX_ID/DB_ROLL_PTR)、Undo Log版本鏈、Read View可見性規(guī)則實(shí)現(xiàn); - 核心邏輯:更新數(shù)據(jù)時(shí)生成版本鏈,讀取數(shù)據(jù)時(shí)通過Read View判斷版本可見性,回溯版本鏈找到符合規(guī)則的版本;
- 隔離級(jí)別差異:RC每次SELECT創(chuàng)建Read View(不可重復(fù)讀),RR僅第一次創(chuàng)建(可重復(fù)讀);
- 核心價(jià)值:實(shí)現(xiàn)“讀不阻塞寫、寫不阻塞讀”,是InnoDB高并發(fā)的核心保障。
到此這篇關(guān)于MySQL MVCC(多版本并發(fā)控制)實(shí)現(xiàn)機(jī)制的文章就介紹到這了,更多相關(guān)MySQL MVCC多版本并發(fā)控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MySQL多版本并發(fā)控制mvcc原理淺析
- 一文詳解MYSQL的多版本并發(fā)控制MVCC(Multi-Version Concurrency Control)
- Mysql InnoDB多版本并發(fā)控制MVCC詳解
- MySQL多版本并發(fā)控制MVCC詳解
- Mysql MVCC多版本并發(fā)控制詳情
- MySQL的多版本并發(fā)控制MVCC的實(shí)現(xiàn)
- MySQL多版本并發(fā)控制MVCC底層原理解析
- MySQL多版本并發(fā)控制MVCC深入學(xué)習(xí)
- mysql多版本并發(fā)控制MVCC的實(shí)現(xiàn)
- MySQL中MVCC多版本并發(fā)控制
相關(guān)文章
DBeaver導(dǎo)入.sql后綴文件詳細(xì)圖文教程
DBeaver是一款數(shù)據(jù)庫管理工具,最重要的是他是一款比較好的開源工具,這篇文章主要介紹了DBeaver導(dǎo)入.sql后綴文件的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2025-12-12
mysql數(shù)據(jù)庫id主鍵自增加刪除后不連續(xù)如何解決
這篇文章主要介紹了mysql數(shù)據(jù)庫id主鍵自增加刪除后不連續(xù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
分組查詢GROUP BY的使用與SQL執(zhí)行順序的講解
今天小編就為大家分享一篇關(guān)于分組查詢GROUP BY的使用與SQL執(zhí)行順序的講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
MySQL表的CURD操作(數(shù)據(jù)的增刪改查)
數(shù)據(jù)庫本質(zhì)上是一個(gè)文件系統(tǒng),通過標(biāo)準(zhǔn)的SQL語句對(duì)數(shù)據(jù)進(jìn)行CURD操作,下面這篇文章主要給大家介紹了關(guān)于MySQL表的CURD操作的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
MySQL實(shí)現(xiàn)數(shù)據(jù)更新的示例詳解
這篇文章主要為大家詳細(xì)介紹了MySQL實(shí)現(xiàn)數(shù)據(jù)更新的相關(guān)資料,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

