为什么MySQL选择Repeatable Read作为默认隔离级别


Posted in MySQL onJuly 26, 2021

关于MySQL的事务隔离级别,相信很多读者都不陌生,网商有很多种相关的文章,很多人对于各种隔离级别,以及不同的级别可以解决的一些读现象都是如数家珍的。

我们知道,ANSI/ISO SQL定义的标准隔离级别有四种,从高到底依次为:可序列化(Serializable)、可重复读(Repeatable Reads)、提交读(Read Committed)、未提交读(Read Uncommitted)。

为什么MySQL选择Repeatable Read作为默认隔离级别

RU隔离级别下,可能发生脏读、幻读、不可重复读等问题。RC隔离级别下,解决了脏读的问题,存在幻读、不可重复读的问题。RR隔离级别下,解决了脏读、不可重复读的问题,存在幻读的问题。Serializable隔离级别下,解决了脏读、幻读、不可重复读的问题。

这四种隔离级别是ANSI/ISO SQL定义的标准定义的,我们比较常用的MySQL对这四种隔离级别是都支持的。但是Oracle数据库只支持Serializable和Read Committed

但是,可能很多人都不知道,Oracle默认的隔离级别是 RC,而MySQL默认的隔离级别是 RR。

那么,你知道为什么Oracle选择RC作为默认级别,而MySQL要选择RR作为默认的隔离级别吗?

这是我之前面试的时候,问过候选人的一个问题。

很多人认为这个问题没有意义,这不是在逼着我们背八股文么?

但是其实并不是,如果你能耐心的看完这篇文章,你就会发现我的良苦用心

Oracle 的隔离级别

前面我们说过,Oracle只只支持ANSI/ISO SQL定义的Serializable和Read Committed,其实,根据Oracle官方文档给出的介绍,Oracle支持三种隔离级别:

为什么MySQL选择Repeatable Read作为默认隔离级别

即Oracle支持Read Committed、Serializable和Read-Only。

Read-Only只读隔离级别类似于可序列化隔离级别,但是只读事务不允许在事务中修改数据,除非用户是SYS。

在Oracle这三种隔离级别中,Serializable和Read-Only显然都是不适合作为默认隔离级别的,那么就只剩Read Committed这个唯一的选择了。

MySQL 的隔离级别

相比于Oracle,MySQL的默认隔离级别的可选范围就比较大了。

首先,我们先从四种隔离级别中排除Serializable和Read Uncommitted这两种,主要是因为这两个级别一个隔离级别太高,一个太低。太高的就会影响并发度,太低的就有脏读现象。

那么,剩下的RR和RC两种,怎么选?

这件事要从long long ago 说起。

在MySQL设计之处,他的定位就是提供一个稳定的关系型数据库。而为了要解决MySQL单点故障带来的问题,MySQL采用主从复制的机制。

所谓主从复制,其实就是通过搭建MySQL集群,整体对外提供服务,集群中的机器分为主服务器(Master)和从服务器(Slave),主服务器提供写服务,从服务器提供读服务。

为了保证主从服务器之间的数据的一致性,就需要进行数据同步,大致的同步过程如下,这里就不详细介绍了

为什么MySQL选择Repeatable Read作为默认隔离级别

MySQL在主从复制的过程中,数据的同步是通过bin log进行的,简单理解就是主服务器把数据变更记录到bin log中,然后再把bin log同步传输给从服务器,从服务器接收到bin log之后,再把其中的数据恢复到自己的数据库存储中。

那么,binlog里面记录的是什么内容呢?格式是怎样的呢?

MySQL的bin log主要支持三种格式,分别是statement、row以及mixed。MySQL是在5.1.5版本开始支持row的、在5.1.8版本中开始支持mixed。

statement和row最大的区别,当binlog的格式为statemen时,binlog 里面记录的就是 SQL 语句的原文(这句话很重要!!!后面会用的到)。

关于这几种格式的区别,就不在这里详细展开了,之所以要支持row格式,主要是因为statement格式中存在很多问题,最明显的就是可能会导致主从数据库的数据不一致。详细介绍可以参考丁奇在极客时间上面的分享《MySQL实战45讲》。

那么,讲这个主从同步和bin log我们要讲的隔离级别有啥关系呢?

有关系,而且关系很大。

因为MySQL早期只有statement这种bin log格式,这时候,如果使用提交读(Read Committed)、未提交读(Read Uncommitted)这两种隔离级别会出现问题。

比如,在MySQL官网上,有人就给官方曾经提过一个相关的Bug

为什么MySQL选择Repeatable Read作为默认隔离级别

这个bug的复现过程如下:

有一个数据库表t1,表中有如下两条记录:

CREATE TABLE t1 (

      a int(11) DEFAULT NULL,

      b int(11) DEFAULT NULL,

      KEY a (a)

    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    insert into t1 values(10,2),(20,1);

接着开始执行两个事务的写操作:

为什么MySQL选择Repeatable Read作为默认隔离级别

以上两个事务执行之后,数据库里面的记录会变成(11,2)和(20,2),这个发上在主库的数据变更大家都能理解。

因为事务的隔离级别是read committed,所以,事务1在更新时,只会对b=2这行加上行级锁,不会影响到事务2对b=1这行的写操作。

以上两个事务执行之后,会在bin log中记录两条记录,因为事务2先提交,所以UPDATE t1 SET b=2 where b=1;会被优先记录,然后再记录UPDATE t1 SET a=11 where b=2;(再次提醒:statement格式的bin log记录的是SQL语句的原文)

这样bin log同步到备库之后,SQL语句回放时,会先执行UPDATE t1 SET b=2 where b=1;,再执行UPDATE t1 SET a=11 where b=2;

这时候,数据库中的数据就会变成(11,2)和(11,2)。这就导致主库和备库的数据不一致了!!!

为了避免这样的问题发生。MySQL就把数据库的默认隔离级别设置成了Repetable Read,那么,Repetable Read的隔离级别下是如何解决这样问题的那?

那是因为Repetable Read这种隔离级别,会在更新数据的时候不仅对更新的行加行级锁,还会增加GAP lock。上面的例子,在事务2执行的时候,因为事务1增加了GAP lock,就会导致事务执行被卡住,需要等事务1提交或者回滚后才能继续执行。(关于GAP lock,我后面会有文章单独介绍)。

除了设置默认的隔离级别外,MySQL还禁止在使用statement格式的bin log的情况下,使用READ COMMITTED作为事务隔离级别。

一旦用户主动修改隔离级别,尝试更新时,会报错:

 ERROR 1598 (HY000): Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'

总结

所以,现在我们知道了,为什么MySQL选择RR作为默认的数据库隔离级别了吧,其实就是为了兼容历史上的那种statement格式的bin log。

那么,本文讲到这里,算是关于MySQL隔离级别这个知识点讲了大概不到1/5吧,通过这篇文章, 你或许还会有以下问题:

1、row格式和statement有什么区别?使用row的情况下,可以使用RR吗?

2、文中提到的RC的GAP lock到底是什么?

3、RR和RC到底有什么区别?RC是如何解决不可重复读问题的?

4、既然MySQL数据库默认选择了RR,那么,为啥像阿里这种大的互联网公司会把默认的隔离级别改成RC?

关于以上几个问题,你知道答案吗,或者你对哪个更感兴趣呢?欢迎留言!我会挑大家较为感兴趣的专题在后面的文章中继续深入展开介绍。

现在你还觉得这个问题没有意义吗?

我其实是想通过这样一个看似无意义的问题,延展出更多的知识,这样可以更加全方位的了解候选人。

到此这篇关于为什么MySQL选择Repeatable Read作为默认隔离级别的文章就介绍到这了,更多相关MySQL Repeatable Read默认隔离级别内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
Mysql Online DDL的使用详解
May 20 MySQL
MySQL大小写敏感的注意事项
May 24 MySQL
Mysql基础知识点汇总
May 26 MySQL
MySQL 不等于的三种使用及区别
Jun 03 MySQL
MySQL 十大常用字符串函数详解
Jun 30 MySQL
mysql事务隔离级别详情
Oct 24 MySQL
MySQL创建管理RANGE分区
Apr 13 MySQL
mysql 8.0.27 绿色解压版安装教程及配置方法
Apr 20 MySQL
MySQL 数据库范式化设计理论
Apr 22 MySQL
MySQL中order by的执行过程
Jun 05 MySQL
MySQL添加索引特点及优化问题
Jul 23 MySQL
MySQL性能指标TPS+QPS+IOPS压测
Aug 05 MySQL
使用ORM新增数据在Mysql中的操作步骤
Jul 26 #MySQL
mysql脏页是什么
Jul 26 #MySQL
为什么MySQL分页用limit会越来越慢
Jul 25 #MySQL
MySQL深度分页(千万级数据量如何快速分页)
ORM模型框架操作mysql数据库的方法
mysql 直接拷贝data 目录下文件还原数据的实现
MySQL索引是啥?不懂就问
You might like
PHP与已存在的Java应用程序集成
2006/10/09 PHP
如何使用Strace调试工具
2013/06/03 PHP
Yii实现多按钮保存与提交的方法
2014/12/03 PHP
Laravel 5框架学习之Eloquent 关系
2015/04/09 PHP
PHP实现动态执行代码的方法
2016/03/25 PHP
thinkPHP5.0框架开发规范简介
2017/03/25 PHP
Prototype使用指南之string.js
2007/01/10 Javascript
javascript之水平横向滚动歌词同步的应用
2007/05/07 Javascript
location.href用法总结(最主要的)
2013/12/27 Javascript
JavaScript中的数组操作介绍
2014/12/30 Javascript
JS实现自动定时切换的简洁网页选项卡效果
2015/10/13 Javascript
理解AngularJs指令
2015/12/10 Javascript
AngularJS基础 ng-disabled 指令详解及简单示例
2016/08/01 Javascript
AngularJS页面传参的5种方式
2017/04/01 Javascript
jQuery实现的简单无刷新评论功能示例
2017/11/08 jQuery
详解小程序退出页面时清除定时器
2019/04/28 Javascript
详解微信小程序支付流程与梳理
2019/07/16 Javascript
解决vue语法会有延迟加载显现{{xxx}}的问题
2019/11/14 Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
2020/08/22 Javascript
Django中的“惰性翻译”方法的相关使用
2015/07/27 Python
Python实现的文本简单可逆加密算法示例
2017/05/18 Python
Python编程实现tail-n查看日志文件的方法
2019/07/08 Python
python3.6+selenium实现操作Frame中的页面元素
2019/07/16 Python
python paramiko远程服务器终端操作过程解析
2019/12/14 Python
Pytorch maxpool的ceil_mode用法
2020/02/18 Python
几个Shell Script面试题
2014/04/18 面试题
部队领导证婚词
2014/01/12 职场文书
微笑服务标语
2014/06/24 职场文书
学生保证书格式
2015/02/27 职场文书
2015年宣传部个人工作总结
2015/05/14 职场文书
周恩来的四个昼夜观后感
2015/06/03 职场文书
导游词之崇武古城
2019/10/07 职场文书
深度学习tensorflow基础mnist
2021/04/14 Python
解决vue $http的get和post请求跨域问题
2021/06/07 Vue.js
关于MybatisPlus配置双数据库驱动连接数据库问题
2022/01/22 Java/Android
GTX1660显卡搭配显示器推荐
2022/04/19 数码科技