Bohr-L Bohr-L
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

刘博

I'm a slow walker, But I never walk backwards.
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 数据处理与存储类

  • Spring 生态类

  • 缓存问题类

  • 多线程类

  • JVM 类

  • MySQL 类

    • MySQL 为什么一定要有一个主键
    • MySQL 中的 RR 隔离级别,到底有没有解决幻读问题
      • MySQL 的行级锁到底锁的是什么东西?
      • 存储 MD5 值应该用 VARCHAR 还是用 CHAR?
      • 数据库的三范式是什么?
      • 说说 InnoDB 与 MyISAM 的区别
      • drop、truncate、delete 的区别
      • 聊一聊数据库事务机制
      • 聊一聊 MySQL 中的关联查询
      • 事务隔离级别有哪些?MySQL 的默认隔离级别是什么
      • 分库分表之后,id 主键如何处理?
      • 说说在 MySQL 中一条查询 SQL 是如何执行的?
      • 讲解下 DDL、DML、DCL
      • 存储过程和触发器的作用
      • MySQL 如何行转列和列转行
      • 如何查看 SQL 的执行计划
      • union 和 unionAll 的区别
      • having 和 where 的区别
      • 常见的索引原则
      • MySQL 中的 IN 和 EXISTS 子句有什么区别?
      • MySQL 如何处理 NULL 值,对性能有什么影响?
      • 如何在 MySQL 中处理和避免全表扫描?
      • MySQL 中的表空间是什么,它的作用是什么?
      • 在 MySQL 中,如何优化 ORDER BY 查询?
    • Java 8 + 特性类

    • 其他技术类

    • 常见面试题
    • MySQL 类
    刘博
    2025-12-29
    目录

    MySQL 中的 RR 隔离级别,到底有没有解决幻读问题

    MySQL 的 RR(Repeatable Read,可重复读)隔离级别在默认配置下(开启innodb_support_xa和 MVCC),解决了 “大部分场景的幻读”,但未完全解决 “所有场景的幻读”,具体分析如下:

    # 1. 先明确:什么是幻读?

    幻读是指 “同一事务内,多次执行相同的查询语句,返回的结果集行数不一致”,通常发生在 “事务 A 查询某范围数据,事务 B 插入 / 删除该范围的数据,事务 A 再次查询时,结果集新增 / 减少了行”,示例:

    • 事务 A(RR 隔离级别):SELECT * FROM user WHERE age > 20,返回 10 行数据;
    • 事务 B:INSERT INTO user (age) VALUES (25),提交事务;
    • 事务 A:再次执行SELECT * FROM user WHERE age > 20,若返回 11 行数据,即发生幻读。

    # 2. RR 隔离级别如何通过 MVCC 解决 “快照读” 的幻读?

    MySQL 的 RR 隔离级别通过MVCC(多版本并发控制) 实现 “快照读”(普通SELECT语句,不加锁),确保同一事务内多次查询的结果集一致,从而解决幻读:

    • MVCC 的核心逻辑:
      1. 事务启动时,会生成一个 “事务 ID(read_view)”,记录当前活跃的事务 ID 范围;
      2. 每行数据都包含DB_TRX_ID(最后修改该数据的事务 ID)和DB_ROLL_PTR(指向 undo 日志的指针,用于恢复历史版本);
      3. 快照读时,InnoDB 会根据read_view筛选数据:仅返回DB_TRX_ID小于当前事务 ID 且未被删除的历史版本数据;
    • 解决幻读的原理:
      • 事务 A 启动后,生成read_view;
      • 事务 B 插入的数据的DB_TRX_ID大于事务 A 的read_view,事务 A 的快照读无法看到该数据;
      • 因此,事务 A 多次执行相同查询,返回的结果集一致,无幻读。

    # 3. RR 隔离级别未解决 “当前读” 的幻读

    “当前读” 是指 “加锁的查询语句”(如SELECT ... FOR UPDATE、SELECT ... LOCK IN SHARE MODE)或 “写操作”(INSERT/UPDATE/DELETE),这类操作会读取数据的 “最新版本”,而非历史版本,因此仍可能发生幻读:

    • 示例(当前读的幻读):
      1. 事务 A(RR 隔离级别):SELECT * FROM user WHERE age > 20 FOR UPDATE,返回 10 行数据(加行锁);
      2. 事务 B:INSERT INTO user (age) VALUES (25),由于事务 A 未对age=25的行加锁(行锁仅锁已存在的行),事务 B 可成功插入并提交;
      3. 事务 A:再次执行SELECT * FROM user WHERE age > 20 FOR UPDATE,返回 11 行数据(包含事务 B 插入的行),发生幻读。

    # 4. 如何完全解决 RR 隔离级别的幻读?

    通过间隙锁(Gap Lock) 可完全解决 RR 隔离级别的幻读,InnoDB 在 RR 隔离级别下默认开启间隙锁(通过innodb_locks_unsafe_for_binlog参数控制,默认关闭,即开启间隙锁):

    • 间隙锁的作用:锁定 “数据之间的间隙”(如age>20的间隙包括20~25、25~30等),防止其他事务在间隙中插入数据;
    • 解决当前读幻读的原理:
      1. 事务 A 执行SELECT * FROM user WHERE age > 20 FOR UPDATE时,InnoDB 不仅对已存在的age>20的行加行锁,还对age>20的间隙加间隙锁;
      2. 事务 B 插入age=25的数据时,需获取该间隙的锁,但间隙锁已被事务 A 持有,事务 B 被阻塞;
      3. 事务 A 再次查询时,无新数据插入,避免幻读。

    # 5. 结论

    • MySQL 的 RR 隔离级别通过 MVCC 解决了 “快照读” 的幻读,通过 “间隙锁 + 行锁”(Next-Key Lock)解决了 “当前读” 的幻读;
    • 在默认配置下(开启间隙锁),RR 隔离级别可完全避免幻读,这是 MySQL RR 隔离级别与其他数据库(如 PostgreSQL)RR 隔离级别的核心区别(其他数据库的 RR 通常无法解决幻读);
    • 若关闭间隙锁(set innodb_locks_unsafe_for_binlog=1),RR 隔离级别仍会发生 “当前读” 的幻读。

    上次更新: 12/30/2025
    MySQL 为什么一定要有一个主键
    MySQL 的行级锁到底锁的是什么东西?

    ← MySQL 为什么一定要有一个主键 MySQL 的行级锁到底锁的是什么东西?→

    最近更新
    01
    CPU 使用率较高排查和解决
    12-29
    02
    JVM OOM 问题如何排查和解决
    12-29
    03
    接口防刷怎么实现?
    12-29
    更多文章>
    Theme by Vdoing | Copyright © 2025-2026 Bohr-L's note
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式