先了解RR(REPEATABLE-READ)和RC(READ-COMMITTED)的区别。
RR隔离级别增加了间隙锁,避免了幻读,并且阻止了不可重复读,让同一个事务里面的查询和修改都是一致的。MySQL默认的隔离级别就是RR。
虽然说RC隔离级别在同一个事务内会存在查询出不同数据的现象,但是这些数据都必然是提交过的,是真实存进硬盘的数据。所以也不用过分担忧,而且RC隔离级别反而降低了锁粒度,也不是毫无用处。Oracle和sql server默认的隔离级别类似RC。
所以说也不是说RC就绝对不好,要看场景来选择,而这里只是简介,不打算深入。
操作流程说明:因系统高并发下,存在多个会话可能同时更新同一条记录的问题,但是值是一样的。问题就在于事务里面存在RR隔离级别转换成RC的问题,造成数据返回不正确,导致代码返回错误,但是数据是准确的。
正常的RR事务
先看当前环境信息:
#当前的mysql版本
mysql> select @@version;
+------------+
| @@version |
+------------+
| 5.6.39-log |
+------------+
1 row in set (0.00 sec)
#当前的隔离级别
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
#当前的binlog格式
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+
1 row in set (0.00 sec)
先看一个正常的事务:
#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)#开启事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)#当前记录是一致的
mysql> select express_cost from m_order_sub where order_sub_no = 'O152022324482662671828';
+--------------+| express_cost |
+--------------+
| 2000 |
+--------------+
1 row in set (0.02 sec)
#当前记录是一致的
mysql> select express_cost from m_order_sub where order_sub_no = 'O152022324482662671828';
+--------------+| express_cost |
+--------------+
| 2000 |
+--------------+
1 row in set (0.02 sec)
#这边先更新一条记录
mysql> update m_order_sub set express_cost = 3000 where order_sub_no = 'O152022324482662671828';
Query OK, 1 row affected (0.01 sec)Rows matched: 1 Changed: 1 Warnings: 0
#这边后更新一条记录,但是另一边并没有commit,所以这边处于等待释放锁
update m_order_sub set express_cost = 3000 where order_sub_no = 'O152022324482662671828';
#这边再查询一次,记录成功修改
mysql> select express_cost from m_order_sub where order_sub_no = 'O152022324482662671828';
+--------------+| express_cost |
+--------------+
| 3000 |
+--------------+
1 row in set (0.00 sec)
#提交事务
mysql> commit;
Query OK, 0 rows affected (0.01 sec)#然后锁释放后这边的更新也执行完了,但是因为更新的值是一样的,所以并没有修改到记录,Changed为0
Query OK, 0 rows affected (12.40 sec)
Rows matched: 1 Changed: 0 Warnings: 0#这边再查询一次,记录成功修改,是最新数据
mysql> select express_cost from m_order_sub where order_sub_no = 'O152022324482662671828';
+--------------+| express_cost |
+--------------+
| 3000 |
+--------------+
1 row in set (0.00 sec)
#这边查询结果是旧的,因为记录并没有被修改到,所以显示的也是事务开始时的数据
mysql> select express_cost from m_order_sub where order_sub_no = 'O152022324482662671828';
+--------------+| express_cost |
+--------------+
| 2000 |
+--------------+
1 row in set (0.00 sec)
#提交并退出事务
mysql> commit;
Query OK, 0 rows affected (0.13 sec)#这时就显示最新的数据了
mysql> select express_cost from m_order_sub where order_sub_no = 'O152022324482662671828';
+--------------+| express_cost |
+--------------+
| 3000 |
+--------------+
1 row in set (0.00 sec)
这是一个正常的情况,因为记录并没有被修改到,所以显示的也是事务开始时的数据,保证了RR级别的可重复读特性。
问题现象
下面来看另一个不正常的情况,环境是和上面一致的,没有改变,我们来直接看图: