MySQL多版本并發(fā)控制mvcc原理淺析
1.mvcc簡(jiǎn)介
1.1mvcc定義
mvcc(Multi Version Concurrency Control),多版本并發(fā)控制,是一種數(shù)據(jù)庫(kù)的并發(fā)控制機(jī)制。它用于管理事務(wù)并發(fā)執(zhí)行時(shí)對(duì)數(shù)據(jù)的訪(fǎng)問(wèn)和修改,保證在多個(gè)事務(wù)同時(shí)對(duì)數(shù)據(jù)庫(kù)進(jìn)行讀寫(xiě)操作,不會(huì)出現(xiàn)數(shù)據(jù)不一致或丟失的情況
1.2mvcc解決的問(wèn)題
當(dāng)多個(gè)事務(wù)同時(shí)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)中的相同數(shù)據(jù)時(shí),可能會(huì)有幾種情況:
- 讀:多個(gè)事務(wù)都是讀操作,不會(huì)產(chǎn)生并發(fā)問(wèn)題
- 讀+寫(xiě):事務(wù)有讀有寫(xiě),那么會(huì)產(chǎn)生臟讀、不可重復(fù)讀、幻讀的問(wèn)題
- 寫(xiě):多個(gè)事務(wù)同時(shí)寫(xiě),可能會(huì)產(chǎn)生數(shù)據(jù)丟失、覆蓋等問(wèn)題
針對(duì)以上問(wèn)題,在讀+寫(xiě)的情況下,通常需要加鎖來(lái)解決問(wèn)題,mysql的innodb實(shí)現(xiàn)了mvcc來(lái)更好的處理讀寫(xiě)沖突,做到不用加鎖,實(shí)現(xiàn)非阻塞并發(fā)讀
在都是寫(xiě)操作的情況下,只能通過(guò)加鎖的方式解決。
1.3當(dāng)前讀與快照讀
當(dāng)前讀:讀取的是最新版本的數(shù)據(jù),保證讀取時(shí)不會(huì)有其他事務(wù)修改數(shù)據(jù),需要對(duì)記錄加鎖
加共享鎖,讀不受影響,寫(xiě)會(huì)被阻塞
select ... lock in share mode;
加排他鎖,讀和寫(xiě)都被阻塞(快照讀不受影響)
select ... for update;
更新、插入、刪除操作以及串行化隔離級(jí)別都是當(dāng)前讀
快照讀:每一次修改數(shù)據(jù),都會(huì)在undolog中存有原始記錄(快照),快照讀就是讀取某一版本的記錄。這種方式能夠不加鎖讀數(shù)據(jù),但是可能會(huì)讀到舊的數(shù)據(jù)。一般的查詢(xún)都是快照讀
select * from tablename;
2.mvcc原理
mvcc主要通過(guò)行記錄中的隱藏字段、undolog和readview實(shí)現(xiàn)的
2.1隱藏字段
mysql的innodb引擎中,在每一行記錄中除了自定義的字段,還有3個(gè)隱藏的字段(innodb引擎)
- row_id:如果表沒(méi)有自定義主鍵,那么會(huì)自動(dòng)生成row_id作為主鍵
- trx_id:記錄修改、新增這條記錄的事務(wù)id
- roll_pointer:回滾指針,指向當(dāng)前記錄的上一個(gè)版本
2.2版本鏈
在修改數(shù)據(jù)時(shí),mysql會(huì)向undolog中記錄數(shù)據(jù)原來(lái)的快照,用于進(jìn)行回滾操作。undolog還能用來(lái)實(shí)現(xiàn)mvcc
如以下例子,mvcc生成版本鏈:
當(dāng)事務(wù)1001(trx_id=1001)執(zhí)行了 insert into user values(1,'竹子',23) 之后:

當(dāng)事務(wù)1002(trx_id=1002)執(zhí)行了 update user set name='竹筍' where id=1 之后:

當(dāng)事務(wù)1003(trx_id=1003)執(zhí)行了 update user set name='竹葉' where id=1 之后:

可以看到,不同版本的數(shù)據(jù)被指針連接起來(lái)形成了一個(gè)鏈表。
當(dāng)我們要讀取時(shí),如何判斷該讀取哪個(gè)版本呢?這就與生成的讀視圖有關(guān)了。
2.3ReadView
讀視圖用于決定事務(wù)可以讀到哪個(gè)版本的數(shù)據(jù)
它包含以下主要信息:
- trx_ids:當(dāng)前mysql中所有活躍的事務(wù)id集合(沒(méi)提交或回滾的事務(wù)集)
- low_limit_id:當(dāng)前出現(xiàn)的最大的事務(wù)id+1,表示下一個(gè)要分配的事務(wù)id
- up_limit_id:當(dāng)前活躍的事務(wù)id集合中,最小的事務(wù)id
- creator_trx_id:生成該ReadView視圖的事務(wù)的id
MySQL5.7版本的源碼對(duì)于這些信息的定義如下:

插入一個(gè)注意事項(xiàng):????????????????????????
start transaction不代表立即生成ReadView,而是在事務(wù)中第一次快照讀的時(shí)候生成ReadView,具體參考MySQL可重復(fù)讀隔離級(jí)別下開(kāi)啟事務(wù)的一個(gè)注意事項(xiàng)
想要開(kāi)啟事務(wù)時(shí)就生成ReadView,請(qǐng)使用:
start transaction with consistent snapshot;
2.4讀視圖生成原則
ReadView定義了一個(gè)可見(jiàn)性算法,當(dāng)事務(wù)進(jìn)行快照讀時(shí),依據(jù)該算法判斷事務(wù)能夠讀取哪個(gè)快照。
源碼的可見(jiàn)性判斷邏輯如下:(下載源碼可訪(fǎng)問(wèn):官網(wǎng),操作系統(tǒng)選擇Source Code)
/** Check whether the changes by id are visible.
@param[in] id transaction id to check against the view
@param[in] name table name
@return whether the view sees the modifications of id. */
//判斷某個(gè)版本的數(shù)據(jù)是否對(duì)當(dāng)前事務(wù)可見(jiàn)
bool changes_visible(
trx_id_t id,
const table_name_t& name) const
MY_ATTRIBUTE((warn_unused_result)) {
ut_ad(id > 0);
//快照的id小于活躍事務(wù)id集合中的最小事務(wù)id 或者 快照的id等于創(chuàng)建這個(gè)視圖的事務(wù)id
if (id < m_up_limit_id || id == m_creator_trx_id) {
return(true);
}
//檢查快照id是否合法,如果快照的id大于等于下一要分配的事務(wù)id,則需要拋出警告信息(會(huì)出現(xiàn)這種情況嗎?)
check_trx_id_sanity(id, name);
//快照的id大于等于下一要分配的事務(wù)id
if (id >= m_low_limit_id) {
return(false);
}
//當(dāng)前不存在活躍的事務(wù)
else if (m_ids.empty()) {
return(true);
}
const ids_t::value_type* p = m_ids.data();
//通過(guò)二分查找判斷快照id是否在活躍事務(wù)集合中,存在則快照不可見(jiàn),不存在則快照可見(jiàn)
return(!std::binary_search(p, p + m_ids.size(), id));
}
- 當(dāng)快照id等于當(dāng)前事務(wù)id時(shí)(trx_id=creator_trx_id),說(shuō)明該版本是當(dāng)前事務(wù)修改的,該快照對(duì)當(dāng)前事務(wù)可見(jiàn)
- 當(dāng)快照id小于活躍事務(wù)的最小id(trx_id<up_limit_id),說(shuō)明該版本對(duì)應(yīng)的事務(wù)已經(jīng)提交了,該快照對(duì)當(dāng)前事務(wù)可見(jiàn)
- 當(dāng)快照id大于等于下一個(gè)要分配的事務(wù)id(trx_id>=low_limit_id),則該快照對(duì)當(dāng)前事務(wù)不可見(jiàn)
- 當(dāng)快照id小于下一個(gè)要分配的事務(wù)id并且活躍事務(wù)id數(shù)量為0(trx_id<low_limit_id && trx_ids.size==0),則該快照對(duì)當(dāng)前事務(wù)可見(jiàn)
- 當(dāng)以上條件都不滿(mǎn)足,則在活躍事務(wù)id集合里查找快照id,如果不存在,則可見(jiàn),否則不可見(jiàn)
3.rc和rr隔離級(jí)別下mvcc的不同
mvcc主要用來(lái)解決rc(讀已提交)隔離級(jí)別下的臟讀和rr(可重復(fù)讀)隔離級(jí)別的不可重復(fù)讀問(wèn)題,所以mvcc只在rc和rr隔離級(jí)別下生效。
區(qū)別在于,rc級(jí)別下,每一次快照讀都會(huì)生成一個(gè)最新的ReadView;RR級(jí)別下,只有事務(wù)中的第一次快照讀會(huì)生成ReadView,之后的快照讀會(huì)使用第一次生成的ReadView。
事務(wù)能否查詢(xún)到其他事物修改的數(shù)據(jù),取決于ReadView,而rc和rr兩個(gè)級(jí)別的ReadView生成方式不同,就導(dǎo)致了事務(wù)可見(jiàn)性不同。(rc級(jí)別下一個(gè)事務(wù)可以查詢(xún)到其他事物在此期間修改并提交的數(shù)據(jù),因?yàn)樗拿看尾樵?xún)都會(huì)生成新的ReadView;rr級(jí)別下事務(wù)無(wú)法查詢(xún)到其他事物在此期間修改并提交的數(shù)據(jù),因?yàn)樗腞eadView只在第一次快照讀生成)
到此這篇關(guān)于MySQL多版本并發(fā)控制mvcc原理淺析的文章就介紹到這了,更多相關(guān)MySQL多版本并發(fā)控制mvcc內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MySQL?MVCC(多版本并發(fā)控制)實(shí)現(xiàn)機(jī)制詳細(xì)講解
- 一文詳解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)文章
mysql遇到load data導(dǎo)入文件數(shù)據(jù)出現(xiàn)1290錯(cuò)誤的解決方案
這篇文章主要介紹了mysql遇到load data導(dǎo)入文件數(shù)據(jù)出現(xiàn)1290錯(cuò)誤的解決方案,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下2018-07-07
MySQL為時(shí)間字段設(shè)置默認(rèn)當(dāng)前時(shí)間的方法技巧
文章詳細(xì)介紹了MySQL中記錄創(chuàng)建時(shí)間和最后修改時(shí)間的最佳實(shí)踐,包括時(shí)間類(lèi)型的支持、默認(rèn)值函數(shù)的使用、MySQL版本的演進(jìn)、常見(jiàn)錯(cuò)誤的修復(fù)以及高級(jí)技巧,建議使用DATETIME或TIMESTAMP類(lèi)型,并在DEFAULT子句中使用CURRENT_TIMESTAMP函數(shù),需要的朋友可以參考下2026-02-02
一文帶你解鎖MySQL實(shí)現(xiàn)行轉(zhuǎn)列的完整方法
MySQL的行轉(zhuǎn)列,不是簡(jiǎn)單的語(yǔ)法堆砌,而是對(duì)數(shù)據(jù)結(jié)構(gòu)深刻理解后的重構(gòu),這篇文章主要介紹了MySQL實(shí)現(xiàn)行轉(zhuǎn)列的完整方法,有需要的小伙伴可以了解下2026-01-01
使用Canal實(shí)現(xiàn)MySQL主從同步的流程步驟
這篇文章主要介紹了如何使用Canal實(shí)現(xiàn)MySQL主從同步效果,文中通過(guò)代碼示例和圖文結(jié)合的方式給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-04-04
MySQL分類(lèi)排名和分組TOP N實(shí)例詳解
大家好,本篇文章主要講的是MySQL分類(lèi)排名和分組TOP N實(shí)例詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下2022-01-01
MySQL?索引簡(jiǎn)介及常見(jiàn)的索引類(lèi)型有哪些
MySQL索引是加速數(shù)據(jù)檢索的特殊結(jié)構(gòu),用于存儲(chǔ)列值與位置信息,常見(jiàn)的索引類(lèi)型包括:主鍵索引、唯一索引、普通索引、復(fù)合索引、全文索引和空間索引等,本文介紹MySQL索引簡(jiǎn)介及常見(jiàn)的索引類(lèi)型有哪些,感興趣的朋友一起看看吧2025-08-08
mysql多個(gè)left join連接查詢(xún)用法分析
這篇文章主要介紹了mysql多個(gè)left join連接查詢(xún)用法,結(jié)合實(shí)例形式對(duì)比分析了mysql使用left join進(jìn)行連接查詢(xún)相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-07-07
在windows環(huán)境下配置并隨意切換兩種mysql版本的方法
這篇文章主要介紹了mysql:如何在windows環(huán)境下配置并隨意切換兩種mysql版本,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
詳解MySQL數(shù)據(jù)庫(kù)千萬(wàn)級(jí)數(shù)據(jù)查詢(xún)和存儲(chǔ)
百萬(wàn)級(jí)、千萬(wàn)級(jí)數(shù)據(jù)處理,核心關(guān)鍵在于數(shù)據(jù)存儲(chǔ)方案設(shè)計(jì),存儲(chǔ)方案設(shè)計(jì)的是否合理,直接影響到數(shù)據(jù)CRUD操作??傮w設(shè)計(jì)可以考慮一下幾個(gè)方面進(jìn)行設(shè)計(jì)考慮:數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)設(shè)計(jì);索引設(shè)計(jì);數(shù)據(jù)主鍵設(shè)計(jì);查詢(xún)方案設(shè)計(jì)。2021-05-05

