RR隔离级别下通过next-key locks 避免幻影读

---恢复内容开始---

mysql innodb目前使用范围最广的两种隔离级别为RC和RR,RR修复了RC中所存在的不可重复读

READ COMMITED

不可重复读

在同一事务中两次查看的结果集不同,如下session1的查看结果集受session2提交的事物影响

05:47:35[test](;)> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

session1
03:28:11[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 03:28:21[test](;)> select * from t1; Empty set (0.01 sec) session2 03:28:27[test](;)> insert into t1 values(1,1,1,1,1); Query OK, 1 row affected (0.00 sec) 03:29:25[test](;)> select * from t1; +----+------+------+------+------+ | id | col1 | col2 | col3 | col4 | +----+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | +----+------+------+------+------+ 1 row in set (0.00 sec)

幻影读(Phantom Rows)

什么是幻影读

同一事务保证了一致性读后,查询时对提交的事务是不可见的,但是变更的时候是可见的,所以就有可能引发幻影读

为什么会导致幻影读

对于update或delete操作(可以使用SELECT with FOR UPDATE or LOCK IN SHARE MODE来进行模拟),innodb只加记录锁(满足条件索引记录)而不是gap锁,gap锁只用于外键约束和duplicate key的检查,所以允许另一事务在锁定的记录旁边自由插入新记录

如col2上有索引,在满足col2>5的条件索引上加索引(对于RC,只在col2为10的索引记录上加X lock),所以在另一会话总插入col2 为非10的记录则会成功

set tx_isolation='read-committed' ;                                                                                  
session 1                                                                                                            
07:46:16[test](;)> begin;                                                                                            
Query OK, 0 rows affected (0.00 sec)                                                                                 
                                                                                                                     
07:46:20[test](;)> select * from t1 where col2>5 for update;                                                         
+----+------+------+------+------+                                                                                   
| id | col1 | col2 | col3 | col4 |                                                                                   
+----+------+------+------+------+                                                                                   
|  2 |    2 |   10 |    2 |    2 |                                                                                   
+----+------+------+------+------+                                                                                   
1 row in set (0.00 sec)                                                                                              
                                                               07:44:48[test](;)> begin;                             
                                                               Query OK, 0 rows affected (0.00 sec)                  
                                                                                                                     
                                                               07:46:36[test](;)> insert into t1 values(3,3,6,3,3);  
                                                               Query OK, 1 row affected (0.00 sec)                   
                                                                                                                     
                                                               07:46:38[test](;)> commit;                            
                                                               Query OK, 0 rows affected (0.10 sec)                  
07:46:31[test](;)> select * from t1 where col2>5;                                                                    
+----+------+------+------+------+                                                                                   
| id | col1 | col2 | col3 | col4 |                                                                                   
+----+------+------+------+------+                                                                                   
|  3 |    3 |    6 |    3 |    3 |                                                                                   
|  2 |    2 |   10 |    2 |    2 |                                                                                   
+----+------+------+------+------+                                                                                   
2 rows in set (0.00 sec)                                                                                             

若col2上无索引,也会是同样的结果现象,如果条件字段上无索引,innodb会先将所有行都加上锁,然后返回给server层根据条件筛选,满足条件则释放行锁,不满足条件则不释放

REPEATABLE READ

如何实现可重复读

事物开始时记录当前的快照版本,事务期间读取的都是事务开始时的快照版本

如何避免幻影读

通过 next-key lock 锁机制,是由index-row lock和gap lock组合成的

如,条件为col2>5,会在区间上[1,5],(5,正无穷)加index-row lock

08:30:02[test](;)> set tx_isolation='repeatable-read' ;    
08:30:49[test](;)> select * from t1;
+----+------+------+------+------+
| id | col1 | col2 | col3 | col4 |
+----+------+------+------+------+
|  1 |    1 |    1 |    1 |    1 |
|  2 |    2 |   10 |    2 |   10 |
+----+------+------+------+------+
2 rows in set (0.00 sec)
Query OK,
0 rows affected (0.00 sec) 08:31:02[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:31:05[test](;)> select * from t1 where col2>5 for update; +----+------+------+------+------+ | id | col1 | col2 | col3 | col4 | +----+------+------+------+------+ | 2 | 2 | 10 | 2 | 10 | +----+------+------+------+------+ 1 row in set (0.00 sec) 08:30:51[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:32:11[test](;)> insert into t1 values(3,3,6,3,6); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

---恢复内容结束---

mysql innodb目前使用范围最广的两种隔离级别为RC和RR,RR修复了RC中所存在的不可重复读

READ COMMITED

不可重复读

在同一事务中两次查看的结果集不同,如下session1的查看结果集受session2提交的事物影响

05:47:35[test](;)> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

session1
03:28:11[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 03:28:21[test](;)> select * from t1; Empty set (0.01 sec) session2 03:28:27[test](;)> insert into t1 values(1,1,1,1,1); Query OK, 1 row affected (0.00 sec) 03:29:25[test](;)> select * from t1; +----+------+------+------+------+ | id | col1 | col2 | col3 | col4 | +----+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | +----+------+------+------+------+ 1 row in set (0.00 sec)

幻影读(Phantom Rows)

什么是幻影读

同一事务保证了一致性读后,查询时对提交的事务是不可见的,但是变更的时候是可见的,所以就有可能引发幻影读

为什么会导致幻影读

对于update或delete操作(可以使用SELECT with FOR UPDATE or LOCK IN SHARE MODE来进行模拟),innodb只加记录锁(满足条件索引记录)而不是gap锁,gap锁只用于外键约束和duplicate key的检查,所以允许另一事务在锁定的记录旁边自由插入新记录

如col2上有索引,在满足col2>5的条件索引上加索引(对于RC,只在col2为10的索引记录上加X lock),所以在另一会话总插入col2 为非10的记录则会成功

set tx_isolation='read-committed' ;                                                                                  
session 1                                                                                                            
07:46:16[test](;)> begin;                                                                                            
Query OK, 0 rows affected (0.00 sec)                                                                                 
                                                                                                                     
07:46:20[test](;)> select * from t1 where col2>5 for update;                                                         
+----+------+------+------+------+                                                                                   
| id | col1 | col2 | col3 | col4 |                                                                                   
+----+------+------+------+------+                                                                                   
|  2 |    2 |   10 |    2 |    2 |                                                                                   
+----+------+------+------+------+                                                                                   
1 row in set (0.00 sec)                                                                                              
                                                               07:44:48[test](;)> begin;                             
                                                               Query OK, 0 rows affected (0.00 sec)                  
                                                                                                                     
                                                               07:46:36[test](;)> insert into t1 values(3,3,6,3,3);  
                                                               Query OK, 1 row affected (0.00 sec)                   
                                                                                                                     
                                                               07:46:38[test](;)> commit;                            
                                                               Query OK, 0 rows affected (0.10 sec)                  
07:46:31[test](;)> select * from t1 where col2>5;                                                                    
+----+------+------+------+------+                                                                                   
| id | col1 | col2 | col3 | col4 |                                                                                   
+----+------+------+------+------+                                                                                   
|  3 |    3 |    6 |    3 |    3 |                                                                                   
|  2 |    2 |   10 |    2 |    2 |                                                                                   
+----+------+------+------+------+                                                                                   
2 rows in set (0.00 sec)                                                                                             

若col2上无索引,也会是同样的结果现象,如果条件字段上无索引,innodb会先将所有行都加上锁,然后返回给server层根据条件筛选,满足条件则释放行锁,不满足条件则不释放

REPEATABLE READ

如何实现可重复读

事物开始时记录当前的快照版本,事务期间读取的都是事务开始时的快照版本

如何避免幻影读

通过 next-key lock 锁机制,是由index-row lock和gap lock组合成的

如,条件为col2>5,会在区间上[1,5],(5,正无穷)加index-row lock

08:30:02[test](;)> set tx_isolation='repeatable-read' ;    
08:30:49[test](;)> select * from t1;
+----+------+------+------+------+
| id | col1 | col2 | col3 | col4 |
+----+------+------+------+------+
|  1 |    1 |    1 |    1 |    1 |
|  2 |    2 |   10 |    2 |   10 |
+----+------+------+------+------+
2 rows in set (0.00 sec)
Query OK,
0 rows affected (0.00 sec) 08:31:02[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:31:05[test](;)> select * from t1 where col2>5 for update; +----+------+------+------+------+ | id | col1 | col2 | col3 | col4 | +----+------+------+------+------+ | 2 | 2 | 10 | 2 | 10 | +----+------+------+------+------+ 1 row in set (0.00 sec) 08:30:51[test](;)> begin; Query OK, 0 rows affected (0.00 sec) 08:32:11[test](;)> insert into t1 values(3,3,6,3,6); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
原文地址:https://www.cnblogs.com/Bccd/p/7543903.html