阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

MySQL中RR隔离级别转换成RC隔离级别案例

128次阅读
没有评论

共计 3163 个字符,预计需要花费 8 分钟才能阅读完成。

先了解 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 级别的可重复读特性。

问题现象

下面来看另一个不正常的情况,环境是和上面一致的,没有改变,我们来直接看图:

MySQL 中 RR 隔离级别转换成 RC 隔离级别案例

可以看到,执行方式和上面一致,右边的事务等待了 12 秒后执行了,也就是左边 commit 之后。但是,变成了不可重复读,右边事务里面没有 commit 也可以看到最新提交的数据,甚是诡异。

解决方法

第一种方案: 将隔离级别改成 RC 貌似是可以解决问题,但是解决的是左边的问题,把可重复读的特性改成了不可重复读了而已。这样两边都能查到已经提交的新数据。

# 更改 mysql 全局隔离级别为 RC

set global tx_isolation = ‘READ-COMMITTED’

改了之后,全局都变成了不可重复读,并且没有了间隙锁,也正因为可以看到已经提交的新数据,所以上面正常的情况也会跟下面一致,但是不代表有问题,这本身就是 RC 隔离级别的特点。

然后有人说,这不是没解决问题嘛,只是把问题全部改成一样而已,好像是这样。所以就有第二种方案。

第二种方案: 把 binlog 格式改成 ROW,不用改隔离级别,问题是真的解决了。

# 把全局 binlog 格式改成 ROW 格式

set global binlog_format = ‘ROW’;

在上面看到原始的的 binlog 格式是 MIXED 混合模式,现在改成 ROW 模式,再试一遍。

MySQL 中 RR 隔离级别转换成 RC 隔离级别案例

好了。一切正常了,这就是 RR 的特性,可重复读。

正文完
星哥说事-微信公众号
post-qrcode
 
星锅
版权声明:本站原创文章,由 星锅 2022-01-22发表,共计3163字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中