MySql,MVCC實現(xiàn)及其機(jī)制,快照讀在RC,RR下的區(qū)別說明
一、什么是MVCC
多版本并發(fā)控制
Multiversion Concurrency Control
大部分的MySQL的存儲 引擎,比如InnoDB,F(xiàn)alcon,以及PBXT并不是簡簡單單的使用行鎖機(jī)制。它們都使用了行鎖結(jié)合一種提高并發(fā)的技術(shù),被稱為MVCC(多版本并 發(fā)控制)。MVCC并不單單應(yīng)用在MySQL中,其他的數(shù)據(jù)庫如Oracle,PostgreSQL,以及其他數(shù)據(jù)庫也使用這個技術(shù)?!?/p>
MVCC避免了許多需要加鎖的情形以及降低消耗。這取決于它實現(xiàn)的方式,它允許非阻塞讀取,在寫的操作的時候阻塞必要的記錄。
MVCC保存了某一時刻數(shù)據(jù)的一個快照。意思就是無論事物運行了多久,它們都能看到一致的數(shù)據(jù)。也就是說在相同的時間下,不同的事物看相同表的數(shù)據(jù)是不同的。如果你從來沒有這方面的經(jīng)驗,可能說這些有點令人困惑。但是在以后這個會很容易理解和熟悉的。
每個存儲引擎實現(xiàn)MVCC方式都是不同的。有許多種包含了樂觀(optimistic)和悲觀(pessimistic)的并發(fā)控制。我們用簡單的InnoDb的行為來舉例說明MVCC工作方式。
InnoDB實現(xiàn)MVCC的方法是,它存儲了每一行的兩個額外的隱藏字段,這兩個隱藏字段分別記錄了行的創(chuàng)建的時間和刪除的時間。在每個事件發(fā)生的時 候,每行存儲版本號,而不是存儲事件實際發(fā)生的時間。每次事物的開始這個版本號都會增加。自記錄時間開始,每個事物都會保存記錄的系統(tǒng)版本號。依照事物的 版本來檢查每行的版本號。在事物隔離級別為可重復(fù)讀的情況下,來看看怎樣應(yīng)用它。
SELECT
- InnoDB檢查每行,要確定它符合兩個標(biāo)準(zhǔn)。
- InnoDB必須知道行的版本號,這個行的版本號至少要和事物版本號一樣的老。(也就是是說它的版本號可能少于或者和事物版本號相同)。這個既能確定事物開始之前行是存在的,也能確定事物創(chuàng)建或修改了這行。
- 行的刪除操作的版本一定是未定義的或者大于事物的版本號。確定了事物開始之前,行沒有被刪除。
- 符合了以上兩點。會返回查詢結(jié)果。
INSERT
- InnoDB記錄了當(dāng)前新增行的系統(tǒng)版本號。
DELETE
- InnoDB記錄的刪除行的系統(tǒng)版本號作為行的刪除ID。
UPDATE
- InnoDB復(fù)制了一行。這個新行的版本號使用了系統(tǒng)版本號。它也把系統(tǒng)版本號作為了刪除行的版本。
所有其他記錄的結(jié)果保存是,從未獲得鎖的查詢。這樣它們查詢的數(shù)據(jù)就會盡可能的快。要確定查詢行要遵循這些標(biāo)準(zhǔn)。缺點是存儲引擎要為每一行存儲更多的數(shù)據(jù),檢查行的時候要做更多的處理以及其他內(nèi)部的一些操作。
MVCC只能在可重復(fù)讀和可提交讀的隔離級別下生效。不可提交讀不能使用它的原因是不能讀取符合事物版本的行版本。它們總是讀取最新的行版本??尚蛄谢荒苁褂肕VCC的原因是,它總是要鎖定行。
下面的表說明了在MySQL中不同鎖的模式以及并發(fā)級別。
我們在操作數(shù)據(jù)庫的時候總是這四大類 讀讀 讀寫 寫讀 寫寫,讀讀肯定是沒有任務(wù)數(shù)據(jù)問題的,但對事物有了解的同學(xué)就會知道,讀寫、寫寫操作很容易就會導(dǎo)致數(shù)據(jù)不一致。
在此之前解決這類問題的常用方式就是 加鎖,聽名字就知道這是個很復(fù)雜、很耗性能的操作,所以大神們不滿足這個操作,從而在MySQL里面實現(xiàn)了MVCC。
MVCC并不是MySQL獨有的,它是一個理念,百度百科解釋如下:
Multi-Version Concurrency Control 多版本并發(fā)控制,MVCC 是一種并發(fā)控制的方法,一般在數(shù)據(jù)庫管理系統(tǒng)中,實現(xiàn)對數(shù)據(jù)庫的并發(fā)訪問;在編程語言中實現(xiàn)事務(wù)內(nèi)存。
MVCC里面有一些關(guān)鍵詞,理解這些關(guān)鍵詞,你就明白了什么是MVCC。MVCC是解決讀寫、寫讀導(dǎo)致數(shù)據(jù)不一致的問題,寫寫問題還是需要加鎖來解決。
所以我們可以使用 MVCC + 鎖(樂觀鎖/悲觀鎖)來解決全部的問題。
二、當(dāng)前讀、快照讀
當(dāng)前讀就是讀取最新的數(shù)據(jù),為了保證讀取的是最新且準(zhǔn)確的數(shù)據(jù),所以它在讀取的時候會加鎖,防止其它事物操作。
快照讀是不加鎖的方式,當(dāng)一個事物要操作數(shù)據(jù)庫的時候,會在這個事物的基礎(chǔ)上形成一個快照,其它的操作就讀取這個快照。
MVCC就是基于快照讀來實現(xiàn)的,在MySQL里面的快照讀是基于這樣幾個關(guān)鍵點來實現(xiàn)的
- 三個隱藏參數(shù) (DB_ROW_ID 隱藏主鍵id、DB_ROLL_PTR 回滾指針、DB_TRX_ID 事物id)
- undo log 日志
- read view
三、隱藏字段
假如我們有一張表,里面有兩個字段,name、age,但實際上我們表里的數(shù)據(jù)是這樣的
3-1、隱藏主鍵
6byte,隱含的自增ID(隱藏主鍵),如果數(shù)據(jù)表沒有主鍵,InnoDB會自動以DB_ROW_ID產(chǎn)生一個聚簇索引
聚簇索引:數(shù)據(jù)存儲和索引是存在一起的,邏輯上和物理上都是一起的,一個表只能有一個聚簇索引。
注:理解聚簇索引可以很好的理解MySQL的索引規(guī)則,感興趣的可以看看這個 MySQL索引詳解
3-2、事物id
記錄這條記錄最后一次操作的事物id
3-3、回滾指針
回滾指針,指向這條記錄的上一個版本(存儲于rollback segment里),用于配合下面的 undo log。
四、undo log
undo log 日志分為兩種
(1)、 insert undo log 數(shù)據(jù)庫在插入數(shù)據(jù)的時候產(chǎn)生,只有在當(dāng)前事物回滾的時候才有用,所以在當(dāng)前事物結(jié)束的時候它就沒用了,就會被刪除。
(2)、 update undo log 數(shù)據(jù)庫在更新、刪除的時候產(chǎn)生,除了當(dāng)前事物會使用,在快照讀的時候也會使用,所以不能隨便刪除,只有在快速讀或事務(wù)回滾不涉及該日志時,對應(yīng)的日志才會被purge線程統(tǒng)一清除。
五、Read View
什么是讀視圖呢?數(shù)據(jù)庫的操作都是多個事物同時進(jìn)行的,有讀有寫。假如當(dāng)前有兩個事物,A事物讀取,B事物正在更新數(shù)據(jù)。
在A事物開始的時候,就形成當(dāng)前數(shù)據(jù)庫的一個快照,記錄并維護(hù)系統(tǒng)當(dāng)前活躍事務(wù)的ID。read view 主要是用來做可見性判斷的,它會判斷每條記錄的的數(shù)據(jù),這條數(shù)據(jù)可能是真實的數(shù)據(jù),也可能是undo log 中的數(shù)據(jù)。
read view 用一個可見性的算法,來判斷當(dāng)前是讀取真實的數(shù)據(jù),還是undo log的數(shù)據(jù)。這里可以簡單理解read view 內(nèi)部維護(hù)了一個事物id列表,里面有最大值和最小值,可以判斷其它事物的id是否在這個可見范圍內(nèi)。
N、其它
N-1、快照讀在RC和RR下的區(qū)別
- RC (read-committed)讀已提交, 可能會導(dǎo)致 不可重復(fù)讀、幻讀
- RR (repeatable-read)可重復(fù)讀,可能會導(dǎo)致 不可重復(fù)讀
幻讀 : 事物A查詢數(shù)據(jù)庫查詢出來了20條數(shù)據(jù),然后事物B刪除了2條數(shù)據(jù),這時候事物A再去查詢發(fā)現(xiàn)只有18條了,從而產(chǎn)生了幻覺。
我們知道在RR級別下面不會產(chǎn)生幻讀,之所以不會產(chǎn)生幻讀,是快照讀在RC和RR下的生成的策略不一樣。
RC隔離級別下,是每個快照讀都會生成并獲取最新的Read View;而在RR隔離級別下,則是同一個事務(wù)中的第一個快照讀才會創(chuàng)建Read View, 之后的快照讀獲取的都是同一個Read View。
MySQL/InnoDB定義的4種隔離級別:
Read Uncommited
- 可以讀取未提交記錄。
Read Committed (RC)
- 針對當(dāng)前讀,RC隔離級別保證對讀取到的記錄加鎖 (記錄鎖),存在幻讀現(xiàn)象
Repeatable Read (RR)(默認(rèn))
- 針對當(dāng)前讀,RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),不存在幻讀現(xiàn)象。
Serializable
- 從MVCC并發(fā)控制退化為基于鎖的并發(fā)控制。不區(qū)別快照讀與當(dāng)前讀,所有的讀操作均為當(dāng)前讀,讀加讀鎖 (S鎖),寫加寫鎖 (X鎖)。
- Serializable隔離級別下,讀寫沖突,因此并發(fā)度急劇下降,在MySQL/InnoDB下不建議使用。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MySQL并發(fā)更新數(shù)據(jù)時的處理方法
在后端開發(fā)中我們不可避免的會遇見MySQL數(shù)據(jù)并發(fā)更新的情況,作為一名后端研發(fā),如何解決這類問題也是必須要知道的,同時這也是面試中經(jīng)??疾斓闹R點。2019-05-05重裝MySQL最后一步失敗的完美解決方案(經(jīng)驗總結(jié))
使用MySQL都有過重裝的經(jīng)歷,要是重裝MySQL基本都是在最后一步通不過,究竟是什么原因呢?下面是我總結(jié)的一點經(jīng)驗,都是血的教訓(xùn)2014-06-06mysql多表join時候update更新數(shù)據(jù)的方法
如果item表的name字段為''就用resource_library 表的resource_name字段前面加上字符串Review更新它,他們的關(guān)聯(lián)關(guān)系在表resource_review_link中。2011-03-03詳解MySQL?substring()?字符串截取函數(shù)
MySQL 查詢數(shù)據(jù)有時候需要對數(shù)據(jù)項進(jìn)行日期格式化或截取特定部分的操作,當(dāng)需要對字符串進(jìn)行截取加工時用到了 substring() 函數(shù),這篇文章主要介紹了MySQL?substring()?字符串截取函數(shù),需要的朋友可以參考下2022-07-07在OneProxy的基礎(chǔ)上實行MySQL讀寫分離與負(fù)載均衡
基于Libevent機(jī)制實現(xiàn),單個實例可以實現(xiàn)25萬的SQL轉(zhuǎn)發(fā)能力,用一個OneProxy節(jié)點可以帶動整個MySQL集群,為業(yè)務(wù)發(fā)展貢獻(xiàn)一份力量,下面由小編來為大家簡單說說2019-05-05