InnoDB的锁机制浅析 (5)

开启第一个事务并执行一次快照查询。

事务一 事务二
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where code > 8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.01 sec)
   
  mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values(9,9);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
 
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where code > 8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.01 sec)
   

这两个事务的执行,有两个问题:

1.为什么之前的例子中,在第二个事务的INSERT被阻塞了,而这次却执行成功了。
这是因为原来的语句中带有for update,这种读取是当前读,会加锁。而本次第一个事务中的SELECT仅仅是快照读,没有加任何锁。所以不会阻塞其他的插入。

2.数据库中的数据已经改变,为什么会读不到?
这个就是之前提到的next-key lock锁定的副本。RC及以下级别才会读到已经提交的事务。更多的业务逻辑是希望在某段时间内或者某个特定的逻辑区间中,前后查询到的数据是一致的,当前事务是和其他事务隔离的。这也是数据库在设计实现时遵循的ACID原则。

再给出RR条件下出现幻读的情形,这种情形不需要两个事务,一个事务就已经可以说明,

mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where id>8; +----+------+ | id | code | +----+------+ | 10 | 10 | +----+------+ 1 row in set (0.00 sec) mysql> update test set code=9 where id=10; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from test where id>8; +----+------+ | id | code | +----+------+ | 10 | 9 | +----+------+ 1 row in set (0.00 sec)

至于RR隔离级别下到底会不会出现幻读,就需要看幻读的定义中的查询到底是连续的查询还是不连续的查询。如果认为RR级别下可能会出现幻读,那该级别下也会出现不重复读。

RR隔离级别下,虽然不会出现幻读,但是会因此产生其他的问题。
前提:当前数据表中只存在(1,1),(5,5),(10,10)三组数据。

如果数据库隔离级别不是默认,可以执行SET SESSION tx_isolation='REPEATABLE-READ';(该语句不是全局设置)更新为RR。

然后执行下列操作:

事务一 事务二 备注
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where code > 8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.01 sec)
    开启事务一,并查询code>8的记录,只有一条(10,10)  
  mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values(11,11);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
  开启第二个事务,插入(11,11)并提交  
mysql> select * from test where code > 8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.01 sec)
    事务一再查询一次,由于RR级别并没有读到更新  
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test values(11,11);
ERROR 1062 (23000): Duplicate entry '11' for key 'PRIMARY'
    事务一明明没有查到,却插入不了  
4.5 更新丢失(Lost Update) 4.5.1 更新丢失

除了上述这类问题外,RR还会有丢失更新的问题。
如下表给出的操作:

事务一 事务二 备注
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where code > 8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.01 sec)
    开启事务一,并查询code>8的记录,只有一条(10,10)  
  mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update test set id=12,code=12 where id=10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
  开启第二个事务,将(10,10)改为(12,12)并提交,注意这里matched是1,changed也是1  
mysql> select * from test where code > 8;
+----+------+
| id | code |
+----+------+
| 10 | 10 |
+----+------+
1 row in set (0.01 sec)
    事务一再次查询code>8的记录,仍然只有一条(10,10)  
mysql> update test set id=9,code=9 where id=10;
Query OK, 0 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
    这里查询到0条,更新了0条  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzsjxy.html