记一个mysql环境rr隔离级别转换成rc的问题

author author     2022-10-24     562

关键词:

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


问题现象

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

技术分享图片

可以看到,执行方式和上面一致,右边的事务等待了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模式,再试一遍.

技术分享图片

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










mysql默认隔离级别是rr,为什么阿里等大厂会改成rc?

△Hollis,一个对Coding有着独特追求的人△这是Hollis的第 377 篇原创分享作者lHollis来源lHollis(ID:hollischuang)我之前写过一篇文章《为什么MySQL选择REPEATABLEREAD作为默认隔离级别?》介绍过MySQL的默认隔离级别是Repeata... 查看详情

mysql默认隔离级别是rr,为什么阿里等大厂会改成rc?

△Hollis,一个对Coding有着独特追求的人△这是Hollis的第 377 篇原创分享作者lHollis来源lHollis(ID:hollischuang)我之前写过一篇文章《为什么MySQL选择REPEATABLEREAD作为默认隔离级别?》介绍过MySQL的默认隔离级别是Repeata... 查看详情

mysqlrr和rc隔离级别区别

MySQL相对于其他数据库来讲,隔离级别是RR,而不是RCRR支持next-keylock,而RC则没有。因为MySQL的RR需要gaplock来解决幻读问题。而RC隔离级别则是允许存在不可重复读和幻读的。所以RC的并发一般要好于RR;RC隔离级别&#... 查看详情

mysqlrr和rc隔离级别区别

MySQL相对于其他数据库来讲,隔离级别是RR,而不是RCRR支持next-keylock,而RC则没有。因为MySQL的RR需要gaplock来解决幻读问题。而RC隔离级别则是允许存在不可重复读和幻读的。所以RC的并发一般要好于RR;RC隔离级别&#... 查看详情

图解mvcc机制

...级别与MySQL一致,Spring中如何修改?简单来说,就是执行一个事务的时候,就生成一个ReadView,里面比较关键的东西有4个:示例:通过undolog多版本链条,加上你开启事务时候生产的一个ReadView,然后再有一个查询的时候,根据Read... 查看详情

mysql默认隔离级别是rr,为什么阿里这种大厂会改成rc?(代码片段)

...之前写过一篇文章《为什么MySQL选择REPEATABLEREAD作为默认隔离级别?》介绍过MySQL的默认隔离级别是RepeatableReads以及背后的原因。主要是因为MySQL在主从复制的过程是通过binlog进行数据同步的,而MySQL早期只有statement这种binl... 查看详情

mysql默认隔离级别是rr,为什么阿里这种大厂会改成rc?(代码片段)

...之前写过一篇文章《为什么MySQL选择REPEATABLEREAD作为默认隔离级别?》介绍过MySQL的默认隔离级别是RepeatableReads以及背后的原因。主要是因为MySQL在主从复制的过程是通过binlog进行数据同步的,而MySQL早期只有statement这种binl... 查看详情

mysql的rr和rc事务隔离级别加锁类型验证(代码片段)

...QL5.7数据库Innodb引擎在默认的REPEATABLE-READ(可重复读RR)事务隔离级别时,事务修改类操作对于where范围条件锁定的行区加的是Next-KeyLock即临键间隙锁,对于确切条件锁定的行加的是RECORDLOCKS即普通行锁,并且无法读到自身事务范围... 查看详情

mysql事务管理(代码片段)

...前读和快照读在RR级别下的区别RR与RC的本质区别CURDCURD是一个 查看详情

mysql幻读&nextkeylock详解

参考技术A幻读的定义是指,一个事务开启后,执行前后两次查询,两次查询中出现了新的数据,幻读仅针对数据的新增。比如:表t中,id为主键,目前有数据1,5,10,20四条。开始一个事务A,前后两次执行select*fromtwhereid<10forupdate... 查看详情

mysql的并发控制与加锁分析

MVCC 隔离级别 幻读死锁 一个简单sql的加锁分析deletefromT1whereid=51.id主键RC2.id唯一索引RC3.id非唯一索引RC4.id无索引RC5.id主键RR6.id唯一索引RR7.id非唯一索引RR8.id无索引RR9.Serialized  查看详情

mysql的事务隔离级别,mvcc,readview和版本链小结

...和roll_pointer(回滚指针)。transactionid:innoDB里面每个事务有一个唯一的事务ID,叫作transactionid,它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。roll_pointer:指向上一事务版本的指针。版本链:是一个单... 查看详情

mysql-8事务与隔离级别io

...上往下依次降低,隔离级别的强度依次增强。下面我们有一个图来进行解释:读取未提交:不可重复读,幻读问题。解决问问题:没有使用事物的时候数据不一致的问题,就是做到知行的sql一起成功,一起回滚。脏读:可以读取... 查看详情

mysql的rr隔离级别与幻读问题

...在网上看了不少mysql锁的文章,不少文章都提到InnoDB的RR隔离级别(RepeatableRead)无法解决幻读的问题。对此问题作者亲自做了一些实验,将实验结论记录在此。本次实验的mysql版本为5.7.22。此篇文章的重点在于通过实验的形式解... 查看详情

怎么避免mysql死锁

...务更倾向于死锁,如果业务允许,将大事务拆小。3.在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。4、降低隔离级别。如果业务允许,将隔离级别调低也是比较好的选择,比如将隔离级别从RR调整为RC,... 查看详情

mysql:rr隔离级别下的幻读

...次分别读到的数据不一致),却存在争议。下面我们来做一个试验:对于下面这张简单的数据表id    num1        112        223       ... 查看详情

锁模块

...所有事务隔离级别在数据库层面上均可避免如下图,对同一个账号进行操作  2)脏读----READ-COMMITED事务隔离级别以上可避免 查询事务的隔离级别SELECT@@tx_isolation;  设置事务的隔离级别SETSESSIONTRANSACTIONISOLATIONLEVELR... 查看详情

mysql事务管理(代码片段)

...c;这一组DML语句要么全部成功,要么全部失败,是一个整体。MySQL提供一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。事务就是要做的或所做的事情,主要用于处理操作量大... 查看详情