數(shù)據(jù)庫事務(wù)中刪除了數(shù)據(jù)還能查到

問題: 有開發(fā)問到,為啥我在一個事務(wù)中刪除數(shù)據(jù)沒有報錯,還能select查到呢? 其實這是隔離級別的原因,不同隔離級別帶來的表現(xiàn)是不同的,下面主要給大家介紹下RC和RR隔離級別的一些原理

本文主要介紹RR和RC在事務(wù)中如何訪問數(shù)據(jù)的,導(dǎo)語中的這個問題我們在最后會說明原因

首先大家需要先了解一個視圖的概念,這個視圖并不是指view這種虛擬表,而是指innodb在實現(xiàn)MVCC時用到的一致性讀視圖,即consistent read view,用于實現(xiàn)RC和RR隔離級別的實現(xiàn)

RR和RC隔離級別下什么時候會產(chǎn)生這個視圖呢?

  • RR:事務(wù)啟動時創(chuàng)建的
    1. 這里需要注意,如果是用begin或者start transaction的方式啟動事務(wù)的話,需要在執(zhí)行第一個操作innodb表的語句才會創(chuàng)建這個視圖
    2. start transaction with consistent snapshot會直接創(chuàng)建這個一致性視圖
  • RC:每個SQL語句開始執(zhí)行的時候創(chuàng)建的

MVCC中不同隔離級別如何訪問數(shù)據(jù)的

RR和RC隔離級別的區(qū)別,先說結(jié)論:

  • RR:
    1、針對查詢:快照讀,以建立的一致性視圖為主,只能查看在一致性快照前就已經(jīng)提交完成的數(shù)據(jù)
    2、針對變更:先進(jìn)行當(dāng)前讀在進(jìn)行變更,每次變更的都是最新的數(shù)據(jù)
  • RC:
    1、所有都是當(dāng)前讀:只能查看在語句啟動前就已經(jīng)提交完成的數(shù)據(jù)

怎么判斷具體哪些數(shù)據(jù)是可以訪問的呢?這里我們需要了解一下在事務(wù)中訪問數(shù)據(jù)時都做了什么操作

  1. innodb 里面每個事務(wù)都有一個唯一的事務(wù)ID,transaction id,在事務(wù)開始時向事務(wù)系統(tǒng)申請的遞增唯一值
  2. 每次數(shù)據(jù)更新都會有一個版本,row trx_id,所以每行數(shù)據(jù)可能會有多個版本,然后多個版本通過undo關(guān)聯(lián)起來,這就是MVCC
  3. RR就是以一致性快照創(chuàng)建的時刻為準(zhǔn),在此時刻前已經(jīng)提交的,可見,自己事務(wù)內(nèi)執(zhí)行的數(shù)據(jù)版本也會認(rèn),數(shù)據(jù)變更需要先獲取最新數(shù)據(jù)
  4. RC就是每個語句執(zhí)行執(zhí)行都會進(jìn)行一次檢查,只要是在我這個語句之前的已經(jīng)提交完成的數(shù)據(jù)就認(rèn)

那么問題來了,如果RR級別下,如何判斷row trx_id是否可見呢

這里innodb為每個事務(wù)都構(gòu)造了一個數(shù)組,用來保存這個一致性快照啟動瞬間,當(dāng)前已經(jīng)啟動未提交的所有事務(wù)id,這個數(shù)組里面里面最小的事務(wù)ID為低水位,事務(wù)ID最大值+1是高水位線,低于低水位線的都是提交了的,高于高水位線的都是未來創(chuàng)建的事務(wù),最低水位線和最高水位線之間的row trx_id
如果在這個數(shù)組內(nèi),則未提交,不在數(shù)組內(nèi),則提交

數(shù)組

一個事務(wù)中,哪些數(shù)據(jù)是可以訪問的呢?

  1. 落在綠色區(qū)域,證明是在一致性視圖創(chuàng)建前已經(jīng)提交的事務(wù)或者自己生成的事務(wù),可見
  2. 落在紅色部分,說明是在一致性視圖創(chuàng)建后啟動的事務(wù),所以不可見
  3. 落在中間部分
    3.1、若 row trx_id 在數(shù)組中,表示這個版本是由還沒提交的事務(wù)生成的,不可見;
    3.2、若 row trx_id 不在數(shù)組中,表示這個版本是已經(jīng)提交了的事務(wù)生成的,可見

這里有個誤區(qū):
很多人會把這個高水位線當(dāng)做當(dāng)前事務(wù)的事務(wù)id+1,但其實不是的,當(dāng)前事務(wù)如果啟動了只做了update操作,也會分配一個事務(wù)id,但是此時卻不會生成read-view,只有在執(zhí)行第一個select時才會產(chǎn)生這個一致性視圖,此時可能也產(chǎn)生了其他事務(wù),所以這個高水位線還是要理解為創(chuàng)建一致性快照時已經(jīng)創(chuàng)建過的最大事務(wù)id+1

表A,只有id,是主鍵(如果不是主鍵RR隔離級別更新就會被鎖住了),數(shù)據(jù)1,2,3,4

時間 事務(wù)A 事務(wù)B 事務(wù)C 事務(wù)D
T1 begin;update A set id=5 where id=1;commit; trx:1
T2 begin ;update A set id=6 where id=2; trx:2
T3 begin; update A set id=7 where id=3;commit; trx 3
T4 begin; update A set id=8 where id=4,; trx:4
T4 select * from A

T4時刻事務(wù)B生成一致性視圖時最低水位線是2,最高水位線就是4+1=5,數(shù)組為(2,4)

  • trx1是低于最低水位線,代表已提交,可見
  • trx2是自己更新的,可見
  • trx3不在數(shù)組中,可見
  • trx4在數(shù)組中,不可見

因此在T4時刻,事務(wù)B讀到的數(shù)據(jù)應(yīng)該就是5,6,7,4

問題解決

我們現(xiàn)在回到開頭的問題

問題:開發(fā)說在一個事務(wù)中刪除了一個數(shù)據(jù),但是select還能看到

  1. begin;select * from aaa id =1;有一個值
  2. delete from aaa where id=1; 無報錯
  3. select * from aaa id =1;還是有那個值

排查原因:開發(fā)是多線程并發(fā)執(zhí)行相同的操作,在某個時間點會出現(xiàn)delete語句執(zhí)行前在另一個事務(wù)已經(jīng)執(zhí)行完了上述操作,將id=1的值已經(jīng)刪除掉了

時間 事務(wù)A 事務(wù)B
T1 begin;select * from aaa where id=1;#有值
T2 begin;delete from aaa where id=1;commit;
T3 delete from aaa where id=1;#這里需要注意這種情況出現(xiàn)的時候返回行數(shù)為0
T4 select * from aaa where id=1;#有值

原因一目了然

  1. 事務(wù)A在T1時刻已經(jīng)建立了一致性視圖,
  2. 事務(wù)B在T2時刻刪除了id=1的數(shù)據(jù)并且提交了
  3. 事務(wù)A在T3時刻進(jìn)行delete操作時進(jìn)行當(dāng)前讀已經(jīng)沒有id=1的數(shù)據(jù),所以雖然沒有報錯但是返回行數(shù)為0
  4. 事務(wù)A在T4時刻select時相當(dāng)于還是進(jìn)行了快照讀,因為在T3時刻相當(dāng)于沒有對事務(wù)進(jìn)行任何修改,所以讀到還是有數(shù)據(jù)

所以這種并行操作時遇到這種情況可以結(jié)合下返回行數(shù)來進(jìn)行判斷,或者隔離級別看是否可以改為RC

本文重點介紹了RR,RC隔離級別下事務(wù)內(nèi)如何訪問數(shù)據(jù)的,一致性快照是何時產(chǎn)生的以及如果結(jié)合一致性快照判斷哪些數(shù)據(jù)是可以訪問的,水平有限,如有理解問題辛苦各位大佬指正

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容