MySQL多版本并發(fā)控制mvcc原理淺析
1.mvcc簡介
1.1mvcc定義
mvcc(Multi Version Concurrency Control),多版本并發(fā)控制,是一種數(shù)據(jù)庫的并發(fā)控制機(jī)制。它用于管理事務(wù)并發(fā)執(zhí)行時對數(shù)據(jù)的訪問和修改,保證在多個事務(wù)同時對數(shù)據(jù)庫進(jìn)行讀寫操作,不會出現(xiàn)數(shù)據(jù)不一致或丟失的情況
1.2mvcc解決的問題
當(dāng)多個事務(wù)同時訪問數(shù)據(jù)庫中的相同數(shù)據(jù)時,可能會有幾種情況:
- 讀:多個事務(wù)都是讀操作,不會產(chǎn)生并發(fā)問題
- 讀+寫:事務(wù)有讀有寫,那么會產(chǎn)生臟讀、不可重復(fù)讀、幻讀的問題
- 寫:多個事務(wù)同時寫,可能會產(chǎn)生數(shù)據(jù)丟失、覆蓋等問題
針對以上問題,在讀+寫的情況下,通常需要加鎖來解決問題,mysql的innodb實(shí)現(xiàn)了mvcc來更好的處理讀寫沖突,做到不用加鎖,實(shí)現(xiàn)非阻塞并發(fā)讀
在都是寫操作的情況下,只能通過加鎖的方式解決。
1.3當(dāng)前讀與快照讀
當(dāng)前讀:讀取的是最新版本的數(shù)據(jù),保證讀取時不會有其他事務(wù)修改數(shù)據(jù),需要對記錄加鎖
加共享鎖,讀不受影響,寫會被阻塞
select ... lock in share mode;
加排他鎖,讀和寫都被阻塞(快照讀不受影響)
select ... for update;
更新、插入、刪除操作以及串行化隔離級別都是當(dāng)前讀
快照讀:每一次修改數(shù)據(jù),都會在undolog中存有原始記錄(快照),快照讀就是讀取某一版本的記錄。這種方式能夠不加鎖讀數(shù)據(jù),但是可能會讀到舊的數(shù)據(jù)。一般的查詢都是快照讀
select * from tablename;
2.mvcc原理
mvcc主要通過行記錄中的隱藏字段、undolog和readview實(shí)現(xiàn)的
2.1隱藏字段
mysql的innodb引擎中,在每一行記錄中除了自定義的字段,還有3個隱藏的字段(innodb引擎)
- row_id:如果表沒有自定義主鍵,那么會自動生成row_id作為主鍵
- trx_id:記錄修改、新增這條記錄的事務(wù)id
- roll_pointer:回滾指針,指向當(dāng)前記錄的上一個版本
2.2版本鏈
在修改數(shù)據(jù)時,mysql會向undolog中記錄數(shù)據(jù)原來的快照,用于進(jìn)行回滾操作。undolog還能用來實(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ù)被指針連接起來形成了一個鏈表。
當(dāng)我們要讀取時,如何判斷該讀取哪個版本呢?這就與生成的讀視圖
有關(guān)了。
2.3ReadView
讀視圖用于決定事務(wù)可以讀到哪個版本的數(shù)據(jù)
它包含以下主要信息:
- trx_ids:當(dāng)前mysql中所有活躍的事務(wù)id集合(沒提交或回滾的事務(wù)集)
- low_limit_id:當(dāng)前出現(xiàn)的最大的事務(wù)id+1,表示下一個要分配的事務(wù)id
- up_limit_id:當(dāng)前活躍的事務(wù)id集合中,最小的事務(wù)id
- creator_trx_id:生成該ReadView視圖的事務(wù)的id
MySQL5.7版本的源碼對于這些信息的定義如下:
插入一個注意事項(xiàng):????????????????????????
start transaction
不代表立即生成ReadView,而是在事務(wù)中第一次快照讀的時候生成ReadView,具體參考MySQL可重復(fù)讀隔離級別下開啟事務(wù)的一個注意事項(xiàng)
想要開啟事務(wù)時就生成ReadView,請使用:
start transaction with consistent snapshot;
2.4讀視圖生成原則
ReadView定義了一個可見性算法,當(dāng)事務(wù)進(jìn)行快照讀時,依據(jù)該算法判斷事務(wù)能夠讀取哪個快照。
源碼的可見性判斷邏輯如下:(下載源碼可訪問:官網(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. */ //判斷某個版本的數(shù)據(jù)是否對當(dāng)前事務(wù)可見 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)建這個視圖的事務(wù)id if (id < m_up_limit_id || id == m_creator_trx_id) { return(true); } //檢查快照id是否合法,如果快照的id大于等于下一要分配的事務(wù)id,則需要拋出警告信息(會出現(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(); //通過二分查找判斷快照id是否在活躍事務(wù)集合中,存在則快照不可見,不存在則快照可見 return(!std::binary_search(p, p + m_ids.size(), id)); }
- 當(dāng)快照id等于當(dāng)前事務(wù)id時(trx_id=creator_trx_id),說明該版本是當(dāng)前事務(wù)修改的,該快照對當(dāng)前事務(wù)可見
- 當(dāng)快照id小于活躍事務(wù)的最小id(trx_id<up_limit_id),說明該版本對應(yīng)的事務(wù)已經(jīng)提交了,該快照對當(dāng)前事務(wù)可見
- 當(dāng)快照id大于等于下一個要分配的事務(wù)id(trx_id>=low_limit_id),則該快照對當(dāng)前事務(wù)不可見
- 當(dāng)快照id小于下一個要分配的事務(wù)id并且活躍事務(wù)id數(shù)量為0(trx_id<low_limit_id && trx_ids.size==0),則該快照對當(dāng)前事務(wù)可見
- 當(dāng)以上條件都不滿足,則在活躍事務(wù)id集合里查找快照id,如果不存在,則可見,否則不可見
3.rc和rr隔離級別下mvcc的不同
mvcc主要用來解決rc(讀已提交)隔離級別下的臟讀和rr(可重復(fù)讀)隔離級別的不可重復(fù)讀問題,所以mvcc只在rc和rr隔離級別下生效。
區(qū)別在于,rc級別下,每一次快照讀都會生成一個最新的ReadView;RR級別下,只有事務(wù)中的第一次快照讀會生成ReadView,之后的快照讀會使用第一次生成的ReadView。
事務(wù)能否查詢到其他事物修改的數(shù)據(jù),取決于ReadView,而rc和rr兩個級別的ReadView生成方式不同,就導(dǎo)致了事務(wù)可見性不同。(rc級別下一個事務(wù)可以查詢到其他事物在此期間修改并提交的數(shù)據(jù),因?yàn)樗拿看尾樵兌紩尚碌腞eadView;rr級別下事務(wù)無法查詢到其他事物在此期間修改并提交的數(shù)據(jù),因?yàn)樗腞eadView只在第一次快照讀生成)
到此這篇關(guān)于MySQL多版本并發(fā)控制mvcc原理淺析的文章就介紹到這了,更多相關(guān)MySQL多版本并發(fā)控制mvcc內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SQL Server 出現(xiàn)Error: 1326錯誤(管理器無法連接遠(yuǎn)程數(shù)據(jù)庫)問題解決方案
這篇文章主要介紹了SQL Server 出現(xiàn)Error: 1326錯誤(管理器無法連接遠(yuǎn)程數(shù)據(jù)庫)問題解決方案的相關(guān)資料,這里對1326 錯誤進(jìn)行了詳細(xì)介紹及解決辦法,需要的朋友可以參考下2016-11-11Mysql錯誤Cannot find or open table x/x from the internal問題解決方法
這篇文章主要介紹了Mysql錯誤Cannot find or open table x/x from the internal問題解決方法,需要的朋友可以參考下2014-06-06MySQL錯誤代碼:1052?Column?'xxx'?in?field?list?is
今天在工作中寫sql語句時遇到了個sql錯誤,為記錄并不再重復(fù)出錯,下面這篇文章主要給大家介紹了關(guān)于MySQL錯誤代碼:1052?Column?'xxx'?in?field?list?is?ambiguous的原因和解決方法,需要的朋友可以參考下2023-04-04select count()和select count(1)的區(qū)別和執(zhí)行方式講解
今天小編就為大家分享一篇關(guān)于select count()和select count(1)的區(qū)別和執(zhí)行方式講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03DataGrip連接Mysql并創(chuàng)建數(shù)據(jù)庫的方法實(shí)現(xiàn)
本文主要介紹了DataGrip連接Mysql并創(chuàng)建數(shù)據(jù)庫的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02Win10系統(tǒng)下MySQL8.0.16 壓縮版下載與安裝教程圖解
這篇文章主要介紹了Win10系統(tǒng)下MySQL8.0.16 壓縮版下載與安裝教程圖解,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考解決價值,需要的朋友可以參考下2019-06-06