先看现象,这里有两个事务,我们假定他们一个是事务A,一个是事务B
这是原本的数据库数据
mysql> select * from account;
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 1 | 张三 | 3000.3 |
| 2 | 李四 | 1000.0 |
| 4 | 王五 | 3000.0 |
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
+----+------+--------+
7 rows in set (0.00 sec)
我们开启事务A和事务B
首先在事务A上进行查询
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 1 | 张三 | 3000.3 |
| 2 | 李四 | 1000.0 |
| 4 | 王五 | 3000.0 |
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
+----+------+--------+
7 rows in set (0.00 sec)
mysql> select * from account where name='zs';
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
+----+------+--------+
4 rows in set (0.00 sec)
此时有4个zs
接下来我们到事务B上进行插入,并且提交事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account (id,name,Money) values(12,'zs',1919);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
可以看到,我插入了一个id=12的用户,并且提交了事务,此时我们再去事务A进行查询
mysql> select * from account where name='zs';
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
+----+------+--------+
4 rows in set (0.00 sec)
可以看到 ,id=12的这个用户在事务A中是查询不到的,如果我们此时在事务A中插入一个id=12的用户会怎么样呢?
mysql> insert into account(id,name,Money) values(12,'zs',1919);
ERROR 1062 (23000): Duplicate entry '12' for key 'account.PRIMARY'
mysql> select * from account where name='zs';
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
+----+------+--------+
4 rows in set (0.00 sec)
此时我们不能插入,用户已经存在了,并且查询的时候,我们并没有发现有这个用户
这种幻读现象是发生在事务隔离级别为可重复读的情况下
所以不可重复读这种隔离级别也不能解决幻读现象
官网有这个解释
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
中文翻译过来就是
当同一查询在不同时间生成不同的行集时,所谓的幻像问题发生在事务中。例如,如果 SELECT 执行了两次,但第二次返回了第一次未返回的行,则该行是“幻像”行。
但是可以看到在不可重复读的事务隔离级别下,这种现象并没有发生,出现的是我们上面所见的那种情况,并没有第二次返回第一次未返回的行这种情况。
也就是说,在innoDB引擎下默认的事务隔离级别(可重复读),不会出现官方说的这种情况。
在可重复读隔离级别下,MVCC机制可以确保同一事务内多次读取同一行数据时,数据的值不会发生变化。如果其他事务对该行数据进行修改或删除操作,当前事务读取的仍是之前的版本,而不会出现新插入或删除的数据行。
读已提交这种事务隔离级别就会出现官方所说的情况
接下来演示这种情况
先修改事务A的事务隔离级别
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED |
+-------------------------+
1 row in set (0.00 sec)
还是上面的数据库,开启两个事务A,B
事务A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account where Money>1200;
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 1 | 张三 | 3000.3 |
| 4 | 王五 | 3000.0 |
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
| 12 | zs | 1919.0 |
| 13 | zs | 1919.0 |
| 14 | zs | 1919.0 |
| 15 | zs | 1919.0 |
+----+------+--------+
此时事务B增加一个数据
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account (id,name,Money) values(16,'zs',1919);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
事务A进行查询
mysql> select * from account where Money>1200;
+----+------+--------+
| id | name | Money |
+----+------+--------+
| 1 | 张三 | 3000.3 |
| 4 | 王五 | 3000.0 |
| 8 | zs | 1919.0 |
| 9 | zs | 1919.0 |
| 10 | zs | 1919.0 |
| 11 | zs | 1919.0 |
| 12 | zs | 1919.0 |
| 13 | zs | 1919.0 |
| 14 | zs | 1919.0 |
| 15 | zs | 1919.0 |
| 16 | zs | 1919.0 |
+----+------+--------+
11 rows in set (0.00 sec)
可以看到第二次查询查到多的一行了,这是读已提交事务隔离级别下的幻读现象,也符合官方描述的那样