MySQL面試題——隔離級別相關面試題

2023-09-14 06:03:21

隔離級別相關面試題

MySQL事務隔離級別

  1. 未提交讀——可以讀到其他事務未提交的資料(最新的版本)
    • 錯誤現象:髒讀、不可重複讀、幻讀的現象
  2. 提交讀(RC)——可以讀到其他事務已提交的資料(最新已提交的版本)
    • 錯誤現象:不可重複讀、幻讀現象
    • 使用場景:希望看到最新的有效值
  3. 可重複讀(RR)——在事務範圍內,多次讀能夠保證一致性(快照建立時最新已提交版本)
    • 錯誤現象:幻讀現象,可以加鎖避免
    • 使用場景:事務內要求更強的一致性,但看到的未必是最新的有效值
  4. 序列讀——在事務範圍內,僅有讀讀可以並行,讀寫或寫寫會阻塞其他事務,用這種辦法可以保證更強的一致性
    • 錯誤現象:無

髒讀

事務1將隔離級別設定成 未提交讀,事務2隔離級別為任意級別;
事務1開啟事務,查詢賬戶資訊,查到id為1的賬號餘額為1000,id為2的餘額為1000;
事務2開啟事務,將id為1的賬戶餘額修改為2000;
事務1再次查詢賬戶資訊,發現與之前查詢的不一致了(此時事務2還未結束)。
上述便是髒讀現象。

不可重複讀

事務1將隔離級別設定成 提交讀,事務2沒有顯示的開啟事務,關閉事務;
事務1開啟事務,查詢賬戶資訊,查到id為1的賬號餘額為1000,id為2的餘額為1000;
事務2將id為1的賬戶餘額修改為2000,因為沒有顯示開啟事務,所以這條sql語句就是一個事務,事務2已經提交;
事務1再次查詢賬戶資訊,發現與之前查詢的不一致了。
上述便是不可重複讀現象。

幻讀

事務1將隔離級別設定成可重複讀,事務2沒有顯示的開啟事務,關閉事務;
事務1開啟事務,查詢賬戶資訊,查到id為1的賬號餘額為1000,id為2的餘額為1000;
事務2插入一條id為3,餘額為1000的資料,因為沒有顯示開啟事務,所以這條sql語句就是一個事務,事務2已經提交;
事務1再次插入id為3,餘額為5000的資料,發現id為3的資料已經存在了,報了主鍵重複。
上述便是幻讀現象。

加鎖避免幻讀

因為事務1要新增id為3的資料,可以給不存在的這條資料查詢時加一把鎖,事務2新增時就會阻塞。

序列讀隔離級別避免幻讀

快照讀與當前讀

當前讀

讀取最新提交的資料

  • select … for update
  • select ... lock in share mode
  • insert、update、delete,都會按最新提交的資料進行操作

當前讀本質上是基於鎖的並行讀操作。

快照讀

讀取某一個快照建立時(可以理解為某一時間點)的資料,也稱為一致性讀。

快照讀主要體現在 select (不帶for update)的語句執行時;不同隔離級別下,select 的行為不同。

  • 在 Serializable 隔離級別下 —— 普通 select 的sql語句也會變成當前讀,即加共用讀鎖;

  • 在 RC 隔離級別下 - 每次 select 都會建立新的快照;

  • 在 RR 隔離級別下

    • 事務啟動後,首次 select 會建立快照
    • 如果事務啟動選擇了 with consistent snapshot,事務啟動時就建立快照
    • 基於舊資料的修改操作,會重新建立快照

快照讀本質上讀取的是歷史資料(原理是回滾段),屬於無鎖查詢。

RR下,快照建立時機——第一次select時

事務1在第一次查詢時建立快照;事務2雖然修改了id為1資料,但是RR下是基於快照讀,是讀到的快照建立時的資料;

如果快照建立比update完會怎樣呢?

事務2先提交了update,事務1select時建立快照,就會讀到事務2修改的資料。

RR下,快照建立時機——事務啟動時

如果想要事務啟動時建立快照,需要新增with consistent snapshot;

雖然表面上事務2比事務1的select先執行,但是事務1開啟事務並建立快照先開始,讀到的是未修改的資料;

RR下,快照建立時機——基於舊資料修改資料時

事務2基於舊資料修改了資料,會重新建立快照,事務1再更新,會基於2000進行修改。