為什么說 MVCC 和 Gap Lock 解決了 MySQL 的幻讀問題


周一的時候被問到了幻讀的問題,之前都是看別人寫的文章沒有建套環境來實際操作一下。

其實很多問題不僅是要看源碼,還是需要動動手,光看還是會忘記。

先說結論在忽略參數設置的情況下, MySQL 的確使用 MVCC 配合 Gap Lock 解決了 RR 隔離級別下的當前讀(用 Gap Lock)和快照讀(用 MVCC)的幻讀問題。

 

我們先來建立測試用的基礎表

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB;

insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

 

MVCC 快照讀解決幻讀

然后 先來看看在 RR 級別下因為 MVCC 的關系解決幻讀的情況

  session1 session2 session3
line 1

begin;

select * from t where d =5;

   
line 2   update t set d = 5 where id = 0  
line 3 select * from t where d =5;    
line 4     insert into t value(1, 1, 5)
line 5 select * from t where d =5;    
line 6 commit;    

line 1 session 1 

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where d = 5;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  5 |    5 |    5 |
+----+------+------+
1 row in set (0.00 sec)

 

line 2 session 2 執行后 line 3 session 1

mysql> update t set d = 5 where id = 0;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from t where d = 5;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  5 |    5 |    5 |
+----+------+------+
1 row in set (0.00 sec)

 

line 4 session3 執行后的 line 5 session 1

mysql> insert into t value (1, 1, 5);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t where d = 5;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  5 |    5 |    5 |
+----+------+------+
1 row in set (0.00 sec)

可以看到在快照讀的情況下, RR 隔離級別可以通過 MVCC 保護自己事務內的數據,無論外面如何修改。

 

Gap Lock 解決當前讀下的幻讀

當前讀的情況下我們就無法達到剛才的效果了,還是剛才那個流程

  session1 session2 session3
line 1

begin;

select * from t where d =5 for update;

   
line 2   update t set d = 5 where id = 0  
line 3 select * from t where d =5 for update;    
line 4     insert into t value(1, 1, 5)
line 5 select * from t where d =5 for update;    
line 6 commit;    

我為 session 1里的所有查詢都加上當前讀的 for update 。會發現從 session2 開始就被阻塞了。

這里就是在執行 select * from t where d = 5 for update 的時候因為 d 上沒有索引的關系,MySQL 直接將該字段的全部范圍都打上了 Gap Lock,所以無法執行插入更新操作,就阻塞住了。

可以看到通過 Gap Lock 我們即使在當前讀也被阻止了幻讀,因為 Gap Lock 的關系 session c 嘗試往里插入新數據也同樣被阻塞從而保證了數據的一致性。

 

所以回到文中開始的那個問題,MySQL 在 RR 事務隔離級別下,的確通過 MVCC 以及 Gap Lock 解決了幻讀問題,是我答錯了。

 


免責聲明!

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



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