• MySQL默认数据库隔离级别为什么是RR?而互联网大厂为什么把它修改为RC?

MySQL默认数据库隔离级别为什么是RR?而互联网大厂为什么把它修改为RC?

2025-08-16 12:33:11 栏目:宝塔面板 0 阅读

前言

大家好,我是田螺

大家都知道mysql的默认数据库隔离级别嘛? 是的,就是RR,但是呢,为什么阿里这些互联网大厂,把mysql的数据隔离级别设置为RC呢?

本文田螺哥,按自己的回答思路,跟大家聊聊~

  • RR和RC的区别
  • mysql的主从复制
  • binlog 日志的三种格式
  • mysql的默认数据库隔离级别为什么是RR
  • 互联网大厂为什么把隔离级别设置为RC

1. RR和RC的区别

大家应该都记得mysql数据库的四种隔离级别吧。

  • RC,也就是读已提交,当前事务只能读取到其他事务提交的数据,所以这种事务的隔离级别解决了脏读问题,但还是会存在重复读、幻读问题。
  • RR,可重复读隔离级别,在一个事务执行期间,无论其他事务如何修改数据,只要本事务未结束,多次读取同一数据集都会获得与事务开始时一致的结果。它解决了不可重复读的问题,但是还是可能存在幻读~~


RR隔离级别,会设置了间隙锁(Gap Lock和 Next-Key Lock),为了尽可能减少幻读。但是特殊场景还是会存在幻读的。

我归纳了它们的主要区别:


RC

RR

数据可见性

可能出现不可重复读

解决了不可重复读问题

锁机制

仅加行锁,无间隙锁,临键锁退化为行锁

过行锁 + 间隙锁 + 临键锁组合控制

是否存在幻读

存在

尽可能解决,特殊情况也存在

性能

并发性能更高,锁冲突少

一致性更强但锁开销大

适合场景

如电商秒杀

适合金融、库存等核心业务

2. mysql的主从复制

在这里先跟大家一起复习一下mysql的主从复制。因为mysql的默认数据库隔离级别,跟主从复制有关系的

主从复制原理,简言之,分三步曲进行:

  • 主数据库有个binlog二进制文件,记录了所有增删改SQL语句。(binlog线程)
  • 从数据库把主数据库的binlog文件的SQL 语句复制到自己的中继日志 relay log(io线程)
  • 从数据库的relay log重做日志文件,再执行一次这些sql语句。(Sql执行线程)

详细的主从复制过程如图:

图片

上图主从复制过程分了五个步骤进行:

  • 主库的更新SQL(update、insert、delete)被写到binlog
  • 从库发起连接,连接到主库。
  • 此时主库创建一个binlog dump thread,把bin log的内容发送到从库。
  • 从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
  • 从库还会创建一个SQL线程,从relay log里面读取内容,从ExecMasterLog_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db

3. binlog日志三种格式

我们再来复习一下binlog日志的三种格式

  • Statement:基于SQL语句的复制((statement-based replication,SBR))
  • Row:基于行的复制。(row-based replication,RBR)
  • Mixed:混合模式复制。(mixed-based replication,MBR)

3.1 Statement格式

每一条会修改数据的sql都会记录在binlog中

  • 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。
  • 缺点:由于记录的只是执行语句,为了这些语句能在备库上正确运行,还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在备库得到和在主库端执行时候相同的结果。

3.2 Row格式

不记录sql语句上下文相关信息,仅保存哪条记录被修改。

  • 优点:binlog中可以不记录执行的sql语句的上下文相关的信息,仅需要记录那一条记录被修改成什么了。所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节。不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题。
  • 缺点:可能会产生大量的日志内容。

3.3 Mixed格式

实际上就是Statement与Row的结合。一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式

4. mysql的默认数据库隔离级别为什么是RR?

MySQL早期,只有statement这种binlog格式。如果mysql的数据库隔离级别设置为RC,在某些场景下,可能数据不一致。

比如,假设有表结构:

-- 创建测试表
CREATE TABLE accounts (
    id INT PRIMARY KEY,
    balance INT 
);

-- 插入初始数据
INSERT INTO accounts (id, balance) VALUES
(1, 20),
(2, 10);

然后在RC的隔离级别下,并发执行这两个事务:

时序

事务A

事务B

1

begin;


2

UPDATE accounts SET id=3 WHERE balance = 20;


3


begin

4


UPDATE accounts SET balance = 20 WHERE balance = 10;

5


commit;

6

commit;


对于主数据库:

以上这个并发事务,执行完后,数据库记录会变为:(3,20)和(2,20)。

如果是对于从库: 因为有statement格式的binlog 日志,记录的是SQL原文,然后事务2的先提交,再执行事务1的。

于是事务2执行完,变为(1,20)和(2,20)。然后事务1执行后,变为(3,20)和(3,20)

其实这就导致数据库的主库和备库数据不一致啦~

因此,MySQL就把数据库的默认隔离级别设置成了Repetable Read(RR)。RR隔离级别下,是如何解决这个问题的呢,其实就是加了GAP锁(间隙锁)。

我们刚那个例子,在事务2执行的时候,因为事务1增加了GAP锁,就会导致事务执行被卡住,需要等事务1提交或者回滚后才能继续执行。

5. 阿里为什么把隔离级别设置为RC

其实,不仅是阿里,现在我们公司,数据库隔离级别吗,也是设置为RC,为什么互联网公司的数据库隔离级别都改为RC呢?

5.1 提升并发

其实就是为了提升访问速度。换几句话说,其实是,并发高的时候,因为

  • RC隔离级别中,不需要Gap锁和Next-Key锁,只是对需要修改的记录加行锁。
  • 而RR隔离级别中,是可能加Gap锁和Next-Key锁的,为了解决幻读问题。

正是因为这个间隙锁的存在,所以RR隔离级别增加了死锁的可能性,如果并发高,耗时就增加了。RR的隔离级别修改为RC隔离级别,并发上来时,整体的耗时是相对更少的。

5.2 修改为RC隔离级别,需要注意哪些问题?

如果修改为RC隔离级别,首先,就需要直面幻读问题啦。

其实也还好,因为很多时候的幻读问题,问题不大的,甚至可以忽略的。或者有时候,可以通过其他手段解决。

其次,设置为RC时,binlog的日志格式,不能设置为statement哈。其实吧,从MySQL是在5.1+版本开始,陆续支持row和mixed的格式啦~~

本文地址:https://www.yitenyun.com/335.html

搜索文章

Tags

数据库 API FastAPI Calcite 电商系统 MySQL Web 应用 异步数据库 数据同步 ACK 双主架构 循环复制 Deepseek 宝塔面板 Linux宝塔 Docker 生命周期 JumpServer JumpServer安装 堡垒机安装 Linux安装JumpServer 序列 核心机制 esxi esxi6 root密码不对 无法登录 web无法登录 SSL 堡垒机 跳板机 HTTPS Windows Windows server net3.5 .NET 安装出错 宝塔面板打不开 宝塔面板无法访问 查看硬件 Linux查看硬件 Linux查看CPU Linux查看内存 连接控制 机制 HTTPS加密 Windows宝塔 Mysql重置密码 无法访问宝塔面板 HexHub 运维 Oracle 处理机制 Serverless 无服务器 语言 Spring Redis 异步化 ES 协同 技术 group by 索引 InnoDB 数据库锁 监控 开源 PostgreSQL 存储引擎 分页查询 高可用 SQL 动态查询 响应模型 缓存方案 缓存架构 缓存穿透 R edis 线程 数据 主库 自定义序列化 服务器 管理口 scp Linux的scp怎么用 scp上传 scp下载 scp命令 日志文件 MIXED 3 查询 SVM Embedding 存储 OB 单机版 Linux 安全 工具 电商 系统 SQLark SQLite-Web SQLite 数据库管理工具 架构 Rsync Postgres OTel Iceberg RocketMQ 长轮询 配置 Recursive 云原生 流量 共享锁 修改DNS Centos7如何修改DNS • 索引 • 数据库 防火墙 黑客 向量数据库 大模型 聚簇 非聚簇 sftp 服务器 参数 PG DBA 缓存 业务 AI 助手 Ftp ​Redis 机器学习 推荐模型 场景 GreatSQL 连接数 优化 万能公式 Netstat Linux 服务器 端口 数据备份 MySQL 9.3 RDB AOF openHalo 数据分类 加密 同城 双活 信息化 智能运维 INSERT COMPACT 窗口 函数 人工智能 推荐系统 核心架构 订阅机制 事务 Java 开发 MVCC 磁盘架构 线上 库存 预扣 redo log 重做日志 mini-redis INCR指令 网络架构 网络配置 Doris SeaTunnel prometheus Alert Redisson 锁芯 向量库 Milvus Undo Log 高效统计 今天这篇文章就跟大家 Python 引擎 性能 网络故障 B+Tree ID 字段 模型 IT运维 Canal Web PostGIS Redis 8.0 不宕机 数据脱敏 加密算法 R2DBC Hash 字段 分布式 集中式 崖山 新版本 自动重启 Pottery OAuth2 Token JOIN Entity ZODB 微软 SQL Server AI功能 容器化 虚拟服务器 虚拟机 内存 MongoDB 数据结构 DBMS 管理系统 容器 数据类型 读写 sqlmock 启动故障 悲观锁 乐观锁 分库 分表 LRU SpringAI 数据页 StarRocks 数据仓库 数据集成工具 分页 单点故障 QPS 高并发 传统数据库 向量化 工具链 Testcloud 云端自动化 Redka filelock 排行榜 排序 发件箱模式 事务隔离 部署 聚簇索引 非聚簇索引 分页方案 排版 大表 业务场景 意向锁 记录锁 速度 服务器中毒 Web 接口 1 SSH 池化技术 连接池 Caffeine CP 仪表盘 分布式架构 分布式锁​ dbt 数据转换工具 日志 原子性 EasyExcel MySQL8 AIOPS MCP 开放协议 优化器 网络 InfluxDB Order IT 双引擎 RAG HelixDB 频繁 Codis Go 数据库迁移 字典 单线程 Ansible 对象 Crash 代码 LLM 事务同步 订单 线程安全 Pump List 类型 UUIDv7 主键 锁机制 Next-Key RR 互联网 语句 服务器性能 TIME_WAIT 负载均衡 播客 行业 趋势 技巧 Weaviate MGR 分布式集群 分布式锁 Zookeeper 并发控制 恢复机制 解锁 调优 ReadView 国产数据库 闪回 产业链 兼容性 国产 用户 快照读 当前读 视图 关系数据库 编程 算法 数据字典 主从复制 代理 GitHub Git count(*) count(主键) 行数 UUID ID 神经系统 慢SQL优化 表空间 千万级 拦截器 动态代理 Valkey Valkey8.0 查询规划 矢量存储 数据库类型 AI代理 恢复数据 CAS 失效 多线程