写点什么

✅为什么 MySQL 默认使用 RR 隔离级别?

作者:派大星
  • 2024-04-25
    江苏
  • 本文字数:1833 字

    阅读完需:约 6 分钟

对于数据库的默认隔离级别,Oracle 默认的隔离级别是 RC,而 MySQL 默认的隔离级别是 RR。


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

Oracle 的隔离级别

Oracle 支持 ANSI/ISO SQL 定义的 Serializable 和 Read Committed 两种隔离级别,根据 Oracle 官方文档的介绍,Oracle 的隔离级别包括 Read Committed、Serializable 和 Read-Only。



Read-Only 的隔离级别类似于 Serializable,然而仅允许只读事务进行数据检索,不允许在事务中修改数据,除非使用者是 SYS 用户。


在 Oracle 的这三种隔离级别中,显而易见,Serializable 和 Read-Only 都不适合作为默认隔离级别,因此唯一的选择就是 Read Committed 了。

MySQL 的隔离级别

与 Oracle 相比,MySQL 提供的默认隔离级别范围更加广泛。


首先,我们排除了 Serializable 和 Read Uncommitted 这两种级别,原因是一个隔离级别过高会影响并发度,另一个过低则存在脏读问题。


剩下的 RR 和 RC 两种,如何选择呢?


MySQL 在设计之初就旨在提供一个稳定的关系型数据库。为解决 MySQL 单点故障问题,MySQL 采取了主从复制机制。


所谓的主从复制,即通过建立 MySQL 集群,以整体向外提供服务。集群内的机器分为主服务器(Master)和从服务器(Slave),主服务器负责提供写服务,而从服务器则提供读服务。


在 MySQL 主从复制过程中,数据的同步通过 binlog 进行。简单来说,主服务器将数据变更记录到 binlog 中,然后将 binlog 同步传输给从服务器。从服务器接收到 binlog 后,将其中的数据恢复到自己的数据库存储中。


那么,binlog 里记录的究竟是什么内容?它的格式又是怎样的呢?


MySQL 的 binlog 主要支持三种格式,即statementrowmixed。MySQL 从 5.1.5 版本开始支持 row 格式,在 5.1.8 版本中开始支持 mixed 格式。


statement 和 row 之间最重要的区别在于,当 binlog 的格式为 statement 时,binlog 记录的是 SQL 语句的原文。


由于 MySQL 早期仅支持 statement 这一种 binlog 格式,因此在使用提交读(Read Committed)和未提交读(Read Uncommitted)这两种隔离级别时都可能会出现问题。


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


CREATE TABLE `t1` (  `a` int(11) DEFAULT NULL,  `b` int(11) DEFAULT NULL,  KEY `b` (`b`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into t1 values(10,1);
复制代码


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



以上两个事务执行之后,数据库里面的记录会只有一条记录(10,99),这个发生在主库的数据变更大家都能理解。


即使 Session 1 的删除操作在 Session 2 的插入操作之后提交,由于 READ COMMITTED 的隔离级别,Session 2 的插入操作不会看到 Session 1 的删除操作,所以最后数据库中仍然会留下 Session 2 插入的记录 (10,99)。

这种行为是 READ COMMITTED 隔禽级别的一种特性,它会在事务开始时创建一个快照。确保事务之间的隔离性,避免了数据不一致性的问题。


以上两个事务执行之后,会在 bin log 中记录两条记录,因为事务 2 先提交,所以insert into t1 values(10,99);会被优先记录,然后再记录delete from t1 where b < 100;再次提醒:statement 格式的 bin log 记录的是 SQL 语句的原文


这样 bin log 同步到备库之后,SQL 语句回放时,会先执行insert into t1 values(10,99);,再执行delete from t1 where b < 100;


这时候,数据库中的数据就会变成 EMPTY SET,即没有任何数据。这就导致主库和备库的数据不一致了!!!


为了解决这种问题,MySQL 将数据库的默认隔离级别设置为 Repeatable Read。在 Repeatable Read 隔离级别下,针对更新数据时会不仅对更新的行加行级锁,还会增加 GAP 锁和 next-key 锁。在上述例子中,当事务 2 执行时,由于事务 1 添加了 GAP 锁和 next-key 锁,这将导致事务 2 执行被阻塞,需要等待事务 1 提交或回滚后才能继续执行。


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


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


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


因此,我们现在明白了为什么 MySQL 选择 Repeatable Read 作为默认的数据库隔离级别了,实际上是为了与历史上那种 statement 格式的 binlog 保持兼容性。


如有问题,欢迎加微信交流:w714771310,备注- 技术交流  。或微信搜索【码上遇见你】。


免费的Chat GPT可微信搜索【AI贝塔】进行体验,无限使用。


好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。

发布于: 刚刚阅读数: 2
用户头像

派大星

关注

微信搜索【码上遇见你】,获取更多精彩内容 2021-12-13 加入

微信搜索【码上遇见你】,获取更多精彩内容

评论

发布
暂无评论
✅为什么MySQL默认使用RR隔离级别?_MySQL_派大星_InfoQ写作社区