Mysql的REPEATABLE-READ能解決幻讀嗎


建表SQL

CREATE TABLE `user` ( `id` int(11) NOT NULL COMMENT 'id', `balance` int(255) DEFAULT NULL COMMENT '余額', `version` int(255) DEFAULT NULL COMMENT '版本號', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_croatian_ci; 

當前的MYSQL的事務隔離級別為,REPEATABLE-READ

select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 

嘗試觸發幻讀

SESSION1 SESSION2
start transaction; start transaction;
select * from user;  
  insert into user values(1, 0, 0);
  commit;
select * from user;  
commit;  
select * from user;  
  • session1開啟事務,並且執行一次檢索,記錄為空
  • session2開啟事務,並且插入一條新記錄,提交
  • session1執行檢索,未讀取到session2插入的新記錄,記錄還是為空
  • session1提交,再次檢索可以成功讀取到session2插入的新記錄

說明 MYSQL 的 REPEATABLE-READ , 其實是可以解決幻讀的問題

但是,不一定能徹底的解決幻讀的問題

SESSION1 SESSION2
start transaction; start transaction;
select * from user where version = 1;  
  insert into user values(2, 0, 1);
  commit;
select * from user where version = 1;  
update user set balance = 1 where version = 1;  
select * from user where version = 1;  
commit;  
  • session1開啟事務,並且根據條件 version = 1 執行一次檢索,記錄為空
  • session2開啟事務,並且插入一條符合session1檢索條件的新記錄,提交
  • session1再次嘗試根據條件 version = 1進行檢索,未讀取到session2插入的新記錄,記錄還是為空
  • session1嘗試修改session2新插入的記錄,修改成功
  • session1執行修改成功后,再次根據條件 version = 1 進行檢索,發現讀取到了session2新插入的記錄

第一種情況可以解決幻讀,而第二種情況就不能解決幻讀,為什么呢?

在MVCC並發控制中,讀操作可以分成兩類:快照讀 (snapshot read)與當前讀 (current read)。
快照讀,讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖。
當前讀,讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再並發修改這條記錄。
在一個支持MVCC並發控制的系統中,哪些讀操作是快照讀?哪些操作又是當前讀呢?以MySQL InnoDB為例:

    快照讀:簡單的select操作,屬於快照讀,不加鎖。(當然,也有例外,下面會分析)
        select * from table where ?;

    當前讀:特殊的讀操作,插入/更新/刪除操作,屬於當前讀,需要加鎖。

        select * from table where ? lock in share mode;
        select * from table where ? for update;
        insert into table values (…);
        update table set ? where ?;
        delete from table where ?;

    所有以上的語句,都屬於當前讀,讀取記錄的最新版本。並且,讀取之后,還需要保證其他並發事務不能修改當前記錄,對讀取記錄加鎖。其中,除了第一條語句,對讀取記錄加S鎖 (共享鎖)外,
其他的操作,都加的是X鎖 (排它鎖)。

 MySQL/InnoDB定義的4種隔離級別:

  • Read Uncommited

    可以讀取未提交記錄。此隔離級別,不會使用,忽略。

  • Read Committed (RC)

    快照讀忽略,本文不考慮。

    針對當前讀,RC隔離級別保證對讀取到的記錄加鎖 (記錄鎖),存在幻讀現象。

  • Repeatable Read (RR)

    快照讀忽略,本文不考慮。

    針對當前讀,RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),不存在幻讀現象。

  • Serializable

    從MVCC並發控制退化為基於鎖的並發控制。不區別快照讀與當前讀,所有的讀操作均為當前讀,讀加讀鎖 (S鎖),寫加寫鎖 (X鎖)。

    Serializable隔離級別下,讀寫沖突,因此並發度急劇下降,在MySQL/InnoDB下不建議使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM