为什么大厂使用的隔离级别是RC,而不是MySQL默认的RR?

MySQL默认的数据隔离级别是RR(可重复读),我一直以为大家都是默认在用RR隔离级别呢,今天查了下我们数据库的默认用的是RC。然后为了解决这个疑问,开始查为什么使用RC?原来很多场景使用RC隔离级别更加合适,譬如互联网大厂一般使用的就是RC隔离级别。

先回忆一下隔离级别

MySQL 事务隔离级别是为了解决并发事务互相干扰的问题的,MySQL 事务隔离级别总共有以下 4 种:

  1. READ UNCOMMITTED:读未提交。
  2. READ COMMITTED:读已提交。
  3. REPEATABLE READ:可重复读。
  4. SERIALIZABLE:序列化。

总结一下,MySQL 中的事务隔离级别就是为了解决脏读、不可重复读和幻读等问题的,这 4 种隔离级别与这 3 个问题之间的对应关系如下:
在这里插入图片描述

MySQL默认级别是RR的有历史原因

我们先来思考一个问题,在Oracle,SqlServer中都是选择读已提交(Read Commited)作为默认的隔离级别,为什么Mysql不选择读已提交(Read Commited)作为默认隔离级别,而选择可重复读(Repeatable Read)作为默认的隔离级别呢?

历史原因

历史原因就要谈到binlog了,数据库的主从复制是基于binlog复制的。binlog有三种格式,分别是:

  • statement:记录的是修改SQL语句
  • row:记录的是每行实际数据的变更
  • mixed:statement和row模式的混合
    在这里插入图片描述

那Mysql在5.0这个版本以前,binlog只支持STATEMENT这种格式!而这种格式在读已提交(Read Commited)这个隔离级别下主从复制是有bug的,因此Mysql将可重复读(Repeatable Read)作为默认的隔离级别! 接下来,就要说说当binlog为STATEMENT格式,且隔离级别为读已提交(Read Commited)时,有什么bug呢?如下图所示,在主(master)上执行如下事务
在这里插入图片描述
此时在主(master)上执行下列语句

select * from test;

输出如下

+---+
| b |
+---+
| 3 |
+---+
1 row in set

但是,你在此时在从(slave)上执行该语句,得出输出如下

Empty set

这样,你就出现了主从不一致性的问题!原因其实很简单,就是在master上执行的顺序为先删后插!而此时binlog为STATEMENT格式,它记录的顺序为先插后删!从(slave)同步的是binglog,因此从机执行的顺序和主机不一致!就会出现主从不一致! 如何解决? 解决方案有两种!
(1)隔离级别设为可重复读(Repeatable Read),在该隔离级别下引入间隙锁。当Session 1执行delete语句时,会锁住间隙。那么,Ssession 2执行插入语句就会阻塞住!
(2)将binglog的格式修改为row格式,此时是基于行的复制,自然就不会出现sql执行顺序不一样的问题!奈何这个格式在mysql5.1版本开始才引入。因此由于历史原因,mysql将默认的隔离级别设为可重复读(Repeatable Read),保证主从复制不出问题!

那么,当我们了解完mysql选可重复读(Repeatable Read)作为默认隔离级别的原因后,接下来我们将其和读已提交(Read Commited)进行对比,来说明为什么在互联网项目为什么将隔离级别设为读已提交(Read Commited)!

RR和RC对比

ok,我们先明白一点!项目中是不用读未提交(Read UnCommitted)和串行化(Serializable)两个隔离级别,原因有二

  • 采用读未提交(Read UnCommitted),一个事务读到另一个事务未提交读数据,这个不用多说吧,从逻辑上都说不过去!
  • 采用串行化(Serializable),每个次读操作都会加锁,快照读失效,一般是使用mysql自带分布式事务功能时才使用该隔离级别!(笔者从未用过mysql自带的这个功能,因为这是XA事务,是强一致性事务,性能不佳!互联网的分布式方案,多采用最终一致性的事务解决方案!)

也就是说,我们该纠结都只有一个问题,究竟隔离级别是用读已经提交呢还是可重复读? 接下来对这两种级别进行对比,讲讲我们为什么选读已提交(Read Commited)作为事务隔离级别!

互联网大厂为什么使用RC

参考文章:https://zhuanlan.zhihu.com/p/59061106

  • **原因1:**在RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大的多!
    在RR隔离级别下,存在间隙锁,可以锁住某个范围间隙,防止其他事务插入数据! 而在RC隔离级别下,不存在间隙锁,其他事务是可以插入数据!

  • **原因2:**在RR隔离级别下,条件列未命中索引会锁表!而在RC隔离级别下,只锁行

  • **原因3:**在RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性
    在5.1.15的时候,innodb引入了一个概念叫做“semi-consistent”,减少了更新同一行记录时的冲突,减少锁等待。 所谓半一致性读就是,一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)!

原因总结:RR隔离级别使用了Gap Lock和Next-Key lock, 发生锁的概率更大,降低了业务的并发性。而RC只有行锁没有间隙锁,所以使用RC可以提升并发度。

RC级别下的不可重复读问题需要解决吗?

不用解决,这个问题是可以接受的!毕竟你数据都已经提交了,读出来本身就没有太大问题!Oracle的默认隔离级别就是RC,你们改过Oracle的默认隔离级别么?

在RC级别下,主从复制用什么binlog格式?

在该隔离级别下,用的binlog为row格式,是基于行的复制!Innodb的创始人也是建议binlog使用该格式!

总结

互联网项目请用:读已提交(Read Commited)这个隔离级别!因为可以提升并发!

  • 26
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值