可重復(fù)讀(repeatable read)定義: 一個事務(wù)執(zhí)行過程中看到的數(shù)據(jù),總是跟這個事務(wù)在啟動時看到的數(shù)據(jù)是一致的。
MVCC
-
MVCC,多版本并發(fā)控制, 用于實(shí)現(xiàn)讀已提交和可重復(fù)讀隔離級別。
-
MVCC的核心就是 Undo log多版本鏈 + Read view,“MV”就是通過 Undo log來保存數(shù)據(jù)的歷史版本,實(shí)現(xiàn)多版本的管理,“CC”是通過 Read-view來實(shí)現(xiàn)管理,通過 Read-view原則來決定數(shù)據(jù)是否顯示。同時針對不同的隔離級別, Read view的生成策略不同,也就實(shí)現(xiàn)了不同的隔離級別。
Undo log 多版本鏈
每條數(shù)據(jù)都有兩個隱藏字段:
-
trx_id: 事務(wù)id,記錄最近一次更新這條數(shù)據(jù)的事務(wù)id.
-
roll_pointer: 回滾指針,指向之前生成的undo log
每一條數(shù)據(jù)都有多個版本,版本之間通過undo log鏈條進(jìn)行連接通過這樣的設(shè)計(jì)方式,可以保證每個事務(wù)提交的時候,一旦需要回滾操作,可以保證同一個事務(wù)只能讀取到比當(dāng)前版本更早提交的值,不能看到更晚提交的值。
ReadView
Read View是 InnoDB 在實(shí)現(xiàn) MVCC 時用到的一致性讀視圖,即 consistent read view,用于支持 RC(Read Committed,讀提交)和 RR(Repeatable Read,可重復(fù)讀)隔離級別的實(shí)現(xiàn).
Read View簡單理解就是對數(shù)據(jù)在某個時刻的狀態(tài)拍成照片記錄下來。那么之后獲取某時刻的數(shù)據(jù)時就還是原來的照片上的數(shù)據(jù),是不會變的.
Read View中比較重要的字段有4個:
-
m_ids
: 用來表示MySQL中哪些事務(wù)正在執(zhí)行,但是沒有提交. -
min_trx_id
: 就是m_ids里最小的值. -
max_trx_id
: 下一個要生成的事務(wù)id值,也就是最大事務(wù)id -
creator_trx_id
: 就是你這個事務(wù)的id
當(dāng)一個事務(wù)第一次執(zhí)行查詢sql時,會生成一致性視圖 read-view(快照),查詢時從 undo log 中最新的一條記錄開始跟 read-view 做對比,如果不符合比較規(guī)則,就根據(jù)回滾指針回滾到上一條記錄繼續(xù)比較,直到得到符合比較條件的查詢結(jié)果。
Read View判斷記錄某個版本是否可見的規(guī)則如下
1.如果當(dāng)前記錄的事務(wù)id落在綠色部分(trx_id < min_id),表示這個版本是已提交的事務(wù)生成的,可讀。 2.如果當(dāng)前記錄的事務(wù)id落在紅色部分(trx_id > max_id),表示這個版本是由將來啟動的事務(wù)生成的,不可讀。
-
如果當(dāng)前記錄的事務(wù)id落在黃色部分(min_id <= trx_id <= max_id),則分為兩種情況:
-
若當(dāng)前記錄的事務(wù)id在未提交事務(wù)的數(shù)組中,則此條記錄不可讀;
-
若當(dāng)前記錄的事務(wù)id不在未提交事務(wù)的數(shù)組中,則此條記錄可讀。
RC 和 RR 隔離級別都是由 MVCC 實(shí)現(xiàn),區(qū)別在于:
-
RC 隔離級別時,read-view 是每次執(zhí)行 select 語句時都生成一個;
-
RR 隔離級別時,read-view 是在第一次執(zhí)行 select 語句時生成一個,同一事務(wù)中后面的所有 select 語句都復(fù)用這個 read-view 。
Repeatable Read 解決了幻讀問題嗎?
可重復(fù)讀(repeatable read)定義: 一個事務(wù)執(zhí)行過程中看到的數(shù)據(jù),總是跟這個事務(wù)在啟動時看到的數(shù)據(jù)是一致的。
不過理論上會出現(xiàn)幻讀,簡單的說幻讀指的的當(dāng)用戶讀取某一范圍的數(shù)據(jù)行時,另一個事務(wù)又在該范圍插入了新行,當(dāng)用戶在讀取該范圍的數(shù)據(jù)時會發(fā)現(xiàn)有新的幻影行。
注意在可重復(fù)讀隔離級別下,普通的查詢是快照讀,是不會看到別的事務(wù)插入的數(shù)據(jù)的。因此, 幻讀在“當(dāng)前讀”下才會出現(xiàn)(查詢語句添加for update,表示當(dāng)前讀);
在 MVCC 并發(fā)控制中,讀操作可以分為兩類: 快照讀(Snapshot Read
)與當(dāng)前讀 (Current Read
)。
-
快照讀 快照讀是指讀取數(shù)據(jù)時不是讀取最新版本的數(shù)據(jù),而是基于歷史版本讀取的一個快照信息(mysql讀取undo log歷史版本) ,快照讀可以使普通的SELECT 讀取數(shù)據(jù)時不用對表數(shù)據(jù)進(jìn)行加鎖,從而解決了因?yàn)閷?shù)據(jù)庫表的加鎖而導(dǎo)致的兩個如下問題
-
解決了因加鎖導(dǎo)致的修改數(shù)據(jù)時無法對數(shù)據(jù)讀取問題.
-
解決了因加鎖導(dǎo)致讀取數(shù)據(jù)時無法對數(shù)據(jù)進(jìn)行修改的問題.
-
-
當(dāng)前讀 當(dāng)前讀是讀取的數(shù)據(jù)庫最新的數(shù)據(jù),當(dāng)前讀和快照讀不同,因?yàn)橐x取最新的數(shù)據(jù)而且要保證事務(wù)的隔離性,所以當(dāng)前讀是需要對數(shù)據(jù)進(jìn)行加鎖的(
插入/更新/刪除操作,屬于當(dāng)前讀,需要加鎖
,select for update
為當(dāng)前讀)
表結(jié)構(gòu)
id | key | value |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
假設(shè) select * from where value=1 for update,只在這一行加鎖(注意這只是假設(shè)),其它行不加鎖,那么就會出現(xiàn)如下場景:
Session A的三次查詢Q1-Q3都是select * from where value=1 for update,查詢的value=1的所有row。
-
T1:Q1只返回一行(1,1,1);
-
T2:session B更新id=0的value為1,此時表t中value=1的數(shù)據(jù)有兩行
-
T3:Q2返回兩行(0,0,1),(1,1,1)
-
T4:session C插入一行(6,6,1),此時表t中value=1的數(shù)據(jù)有三行
-
T5:Q3返回三行(0,0,1),(1,1,1),(6,6,1)
-
T6:session A事物commit。
其中Q3讀到value=1這一樣的現(xiàn)象,就稱之為幻讀,幻讀指的是一個事務(wù)在前后兩次查詢同一個范圍的時候,后一次查詢看到了前一次查詢沒有看到的行。
先對“幻讀”做出如下解釋:
-
要討論「可重復(fù)讀」隔離級別的幻讀現(xiàn)象,是要建立在「當(dāng)前讀」的情況下,而不是快照讀,因?yàn)樵诳芍貜?fù)讀隔離級別下,普通的查詢是快照讀,是不會看到別的事務(wù)插入的數(shù)據(jù)的。
Next-key Lock 鎖
產(chǎn)生幻讀的原因是,行鎖只能鎖住行,但是新插入記錄這個動作,要更新的是記錄之間的“間隙”。因此,Innodb 引擎為了解決「可重復(fù)讀」隔離級別使用「當(dāng)前讀」而造成的幻讀問題,就引出了 next-key 鎖,就是記錄鎖和間隙鎖的組合。
-
RecordLock鎖:鎖定單個行記錄的鎖。(記錄鎖,RC、RR隔離級別都支持)
-
GapLock鎖:間隙鎖,鎖定索引記錄間隙(不包括記錄本身),確保索引記錄的間隙不變。(范圍鎖,RR隔離級別支持)
-
Next-key Lock 鎖:記錄鎖和間隙鎖組合,同時鎖住數(shù)據(jù),并且鎖住數(shù)據(jù)前后范圍。(記錄鎖+范圍鎖,RR隔離級別支持)
總結(jié)
-
RR隔離級別下間隙鎖才有效,RC隔離級別下沒有間隙鎖;
-
RR隔離級別下為了解決“幻讀”問題:“快照讀”依靠MVCC控制,“當(dāng)前讀”通過間隙鎖解決;
-
間隙鎖和行鎖合稱next-key lock,每個next-key lock是前開后閉區(qū)間;
-
間隙鎖的引入,可能會導(dǎo)致同樣語句鎖住更大的范圍,影響并發(fā)度。文章來源:http://www.zghlxwxcb.cn/news/detail-698109.html
知識來源:馬士兵教育文章來源地址http://www.zghlxwxcb.cn/news/detail-698109.html
到了這里,關(guān)于java八股文面試[數(shù)據(jù)庫]——可重復(fù)讀怎么實(shí)現(xiàn)的(MVCC)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!