幻讀是指多事務並發中一個事務讀到了另一個事務insert的記錄。
在REPEATABLE READ隔離級別下,假設事務T1執行后,事務T2開始執行,並新增一條記錄,然后事務T2提交,這時在事務T1中執行select是看不到事務T2新增的這條記錄的。因為在事務T1生成readview的時刻,事務T2屬於未來事務,所以是看不到事務T2新增的這條記錄的。
假設有如下場景:
# 表結構如下
CREATE TABLE hero (
number INT,
name VARCHAR(100),
country varchar(100),
PRIMARY KEY (number),
KEY idx_name (name)
) Engine=InnoDB CHARSET=utf8;
# 事務T1,REPEATABLE READ隔離級別下
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM hero WHERE number = 30;
Empty set (0.01 sec)
# 此時事務T2執行了:INSERT INTO hero VALUES(30, 'g關羽', '魏'); 並提交
mysql> UPDATE hero SET country = '蜀' WHERE number = 30;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM hero WHERE number = 30;
+--------+---------+---------+
| number | name | country |
+--------+---------+---------+
| 30 | g關羽 | 蜀 |
+--------+---------+---------+
1 row in set (0.01 sec)
在REPEATABLE READ隔離級別下,T1第一次執行普通的SELECT語句時生成了一個ReadView,之后T2向hero表中新插入了一條記錄便提交了,ReadView並不能阻止T1執行UPDATE或者DELETE語句來對改動這個新插入的記錄(因為T2已經提交,改動該記錄並不會造成阻塞),但是這樣一來這條新記錄的trx_id隱藏列就變成了T1的事務id,之后T1中再使用普通的SELECT語句去查詢這條記錄時就可以看到這條記錄了,也就把這條記錄返回給客戶端了。因為這個特殊現象的存在,你也可以認為InnoDB中的MVCC並不能完完全全的禁止幻讀。
