因此,在RR中,以上测试会话1、会话2的依次执行,与Binlog的顺序一致,从而保证DB/DR一致。
幻象读除了保证可重复读,MySQL的RR还一定程度上避免了幻象读(幻象读是由于插入导致的新记录)。(为什么说一定程度呢?参考第3节可重复读和串行化的区别。)
会话1
会话2
use test;
#初始化数据
create table t1(c1 int primary key, c2 int) engine=innodb;
create table t2(c1 int primary key, c2 int) engine=innodb;
insert into t1 values(1,1), (10,10);
insert into t2 values(1,1), (5,5), (10,10);
#设置隔离级别
set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
#连续更新两次
mysql> Begin;
Query OK, 0 rows affected (0.03 sec)
mysql> update t2 set c2 = 20 where c1 in (select c1 from t1);
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> delete from where c1 in (select c1 from t1);
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from t2;
+------+------+
| c1 | c2 |
+------+------+
| 5 | 5 |
+------+------+
2 rows in set (0.00 sec)
mysql> commit;
#设置隔离级别
set tx_isolation=' repeatable-read';
Query OK, 0 rows affected (0.00 sec)
#两次更新之间执行插入
mysql> insert into t1 values(5,5);
--阻塞,直到会话1提交
Query OK, 1 row affected (18.94 sec)
由上述例子知,会话2的插入操作被阻塞了,原因是RR隔离级别中,除了记录锁外,还会上间隙锁(gap锁)。例如,对于表t1,update t2 set c2 = 20 where c1 in (select c1 from t1)以上的锁包括:
(-∞, 1), 1, (1, 10), 10, (10, +∞)
由于对t1做全表扫描,因此,所有记录和间隙都要上锁,其中(x,y)表示间隙锁,数字表示记录锁,全部都是S锁。会话2的insert操作插入5,位于间隙(1,10),需要获得这个间隙的X锁,因此两操作互斥,会话2阻塞。
SQL标准的RR并不要求避免幻象读,而InnoDB通过gap锁来避免幻象,���而实现SQL的可串行化,保证Binlog的一致性。
要想取消gap lock,可使用参数innodb_lock_unsafe_for_binlog=1,默认为0。
3. 可重复读与串行化的区别InnoDB的RR可以避免不可重复读和幻象读,那么与串行化有什么区别呢?
会话1
会话2
use test;
#初始化数据
create table t3(c1 int primary key, c2 int) engine=innodb;
#设置隔离级别
set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql> Begin;
Query OK, 0 rows affected (0.03 sec)
mysql> select * from t3 where c1 = 1;
Empty set (0.00 sec)
mysql> select * from t3 where c1 = 1;
Empty set (0.00 sec)
mysql> update t3 set c2 =2 where c1 = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from t3 where c1 = 1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 2 |
+----+------+
1 row in set (0.00 sec)
mysql> commit;
#设置隔离级别
set tx_isolation=' repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t3 values(1,1);
Query OK, 1 row affected (0.05 sec)
由上述会话1中,连续两次读不到数据,但更新却成功,并且更新后的相同读操作就能读到数据了,这算不算幻读呢?