接下来我们学习Mysql默认的事务隔离级别Repeatable Read,顾名思义,可重复读,也即在一个事务范围内相同的查询会返回相同的数据。
延续上面的栗子:
1. 小明很开心自己考了69分,于是他连接到数据库查询自己的成绩来炫耀给小伙伴,由于Repeatable Read是默认的事务隔离级别,因此这次他不需要进行修改:
xiaoming> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
xiaoming> begin;
Query OK, 0 rows affected (0.00 sec)
xiaoming> select * from scores where name = 'xiaoming';
+----+----------+-------+
| id | name | score |
+----+----------+-------+
| 1 | xiaoming | 69 |
+----+----------+-------+
1 row in set (0.00 sec)
2. 不幸的是,小明的班主任王老师复查试卷后,发现小明的成绩多加了10分,于是他连接到数据库来修改小明的成绩:
mr.wang> begin;
Query OK, 0 rows affected (0.00 sec)
mr.wang> update scores set score = 59 where name = 'xiaoming';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mr.wang> commit;
Query OK, 0 rows affected (0.00 sec)
3. 接着小明觉得还不尽兴,于是又查一次,还是69分,可怜的是他不知道自己其实是不及格的:
xiaoming> select * from scores where name = 'xiaoming';
+----+----------+-------+
| id | name | score |
+----+----------+-------+
| 1 | xiaoming | 69 |
+----+----------+-------+
1 row in set (0.00 sec)
可见Repeatable Read的确可以解决“不可重复读”的问题,小明在一次事务中2次查询的成绩都是一样的,即使2次查询中王老师修改了成绩。注意我们演示的场景中,王老师是针对一条已有的记录进行了Update, 如果王老师是新增即Insert小明的成绩,那么小明的2次查询的结果还是不一样的,如下所示:
1.首先小明第一次查询, 没有成绩
xiaoming> select * from scores where name = 'xiaoming';
Empty set (0.00 sec)
2. 然后王老师录入成绩
mr.wang> begin;
Query OK, 0 rows affected (0.00 sec)
mr.wang> insert into scores(name,score) values ("xiaoming", 59);
Query OK, 1 row affected (0.00 sec)
mr.wang> commit;
Query OK, 0 rows affected (0.00 sec)
3. 最后小明再次查询成绩,这次有了:
xiaoming> select * from scores where name = 'xiaoming';
+----+----------+-------+
| id | name | score |
+----+----------+-------+
| 1 | xiaoming | 59 |
+----+----------+-------+
1 row in set (0.00 sec)
通过上述例子,我们可以看出Repeatable Read也是存在以下问题的:
a. 幻读,也即在一次事务范围内多次进行查询,如果其他并发事务中途插入了新的记录,那么之后的查询会读取到这些“幻影”行。
另外,我们也需要注意,不可重复读对应的是修改即Update,幻读对应的是插入即Insert。