当前位置: 首页 > backend >正文

RR(Repeatable Read)级别如何防止幻读

在这里插入图片描述

在 MySQL 数据库事务隔离级别中,RR(可重复读) 通过 MVCC(多版本并发控制)锁机制 的组合策略来避免幻读问题。


一、MVCC机制:快照读与版本控制

  1. 快照读(Snapshot Read)

    • 每个事务启动时生成一个全局唯一的 事务版本号,所有 SELECT 操作基于该版本号读取数据的快照(历史版本),而非最新数据。
    • 快照规则
      • 仅读取 创建版本号 ≤ 当前事务版本号 的数据行。
      • 忽略 删除版本号 ≤ 当前事务版本号 或未标记删除的数据行。
    • 效果:同一事务内多次读取同一范围的数据时,结果一致(即使其他事务插入新数据并提交)。
  2. 版本更新规则

    • INSERT:新插入行的创建版本号设为当前事务版本号。
    • DELETE:标记删除行的删除版本号为当前事务版本号。
    • UPDATE:拆分为删除旧行(标记删除版本号)和插入新行(设置新创建版本号)。

二、锁机制:当前读与间隙锁

  1. 当前读(Current Read)

    • 对数据修改操作(UPDATE/INSERT/DELETE/SELECT FOR UPDATE),强制读取最新数据版本并加锁。
    • 锁类型
      • 记录锁(Row Lock):锁定单行数据。
      • 间隙锁(Gap Lock):锁定索引记录间的间隙,阻止其他事务插入新数据。
      • 临键锁(Next-Key Lock):记录锁 + 间隙锁的组合,锁定一个左开右闭的区间(如 (5, 10])。
  2. 间隙锁的作用

    • 防止范围插入:例如事务 A 查询 age BETWEEN 20 AND 30 的数据并加间隙锁,事务 B 尝试插入 age=25 的数据会被阻塞,直到事务 A 提交或回滚。
    • 覆盖索引与非唯一索引:即使查询条件未命中实际数据行,间隙锁仍会锁定符合条件的索引范围。

三、MySQL InnoDB 的特殊实现

  1. RR 与 Serializable 的折中

    • 快照读:通过 MVCC 避免幻读(如普通 SELECT 操作)。
    • 当前读:通过间隙锁强制串行化,阻止其他事务插入新数据。
    • 实际效果:在 MySQL 的 RR 级别下,几乎不会出现幻读(与标准 SQL 定义的 RR 存在差异)。
  2. 示例场景

    -- 事务 A
    START TRANSACTION;
    SELECT * FROM users WHERE age BETWEEN 20 AND 30; -- 快照读,无幻读
    UPDATE users SET name='Alice' WHERE age=25;      -- 当前读触发间隙锁
    -- 事务 B 插入 age=25 的数据;-- 因为间隙锁存在,阻塞事务 B 的插入
    COMMIT;
    
  3. mysql 幻读问题
    mysql幻读是通过MVCC和临键锁、间隙锁避免的,但在一些特殊场景下,还是会出现幻读问题:事务中快照读当前读同时存在时就会出现幻读问题

    -- 事务A
    BEGIN;
    SELECT * FROM users WHERE age > 20;  -- 快照读,未加锁
    -- 事务B插入age=25的数据并提交
    -- 事务A执行当前读
    SELECT * FROM users WHERE age > 20 FOR UPDATE;  -- 当前读,返回包含事务B插入的新数据,发生幻读
    

四、与其他数据库的对比

数据库RR 实现方式是否完全避免幻读
MySQL InnoDBMVCC + 间隙锁是(近似)
PostgreSQLMVCC(无间隙锁)
Oracle多版本读一致性(无间隙锁)

五、应用注意事项

  1. 显式加锁的必要性

    • 若需完全避免幻读,应在事务中对查询范围显式加锁(如 SELECT ... FOR UPDATE)。
    • 仅依赖快照读可能无法覆盖高并发场景下的严格一致性需求。
  2. 性能权衡

    • 间隙锁的代价:锁定范围可能降低并发写入性能。
    • 索引设计优化:合理设计索引(如唯一索引)可减少间隙锁的覆盖范围。

总结

MySQL 的 RR 隔离级别通过 MVCC 快照读间隙锁 的组合,在大多数场景下有效避免了幻读问题。其实现机制兼顾了性能与一致性,但需注意不同数据库的差异及实际业务中显式锁的使用需求。对于严格防幻读的场景,可升级至 Serializable 级别或通过应用层逻辑补充控制。

http://www.xdnf.cn/news/3887.html

相关文章:

  • 31.软件时序控制方式抗干扰
  • maven坐标导入jar包时剔除不需要的内容
  • C++类_协变返回类型
  • 【KWDB 创作者计划】_KWDB 性能优化与调优
  • redis的持久化
  • Spring的循环依赖问题
  • 工业认知智能:从数据分析到知识创造
  • 自由学习记录(58)
  • Android逆向学习(八)Xposed快速上手(上)
  • GitLab CI/CD变量使用完全指南
  • 修复笔记:SkyReels-V2 项目中的 torch.cuda.amp.autocast 警告和错误
  • 2025年- H24-Lc132-94. 二叉树的中序遍历(树)---java版。
  • 施磊老师rpc(四)
  • QT开发工具对比:Qt Creator、Qt Designer、Qt Design Studio
  • Redis 数据类型详解(一):String 类型全解析
  • RabbitMQ 深度解析:从核心组件到复杂应用场景
  • nt!MiSessionAddProcess函数分析和nt!MmSessionSpace全局变量的关系
  • DeepSeek Copilot idea插件推荐
  • 架构思维:使用懒加载架构实现高性能读服务
  • 运算放大器的主要技术指标
  • 【浅尝Java】变量与数据类型(含隐式类型转换、强制类型转换、整型与字符串互相转换等)
  • JWT解析
  • WebRTC 服务器之Janus视频会议插件信令交互
  • docker:制作镜像+上传镜像+拉取镜像
  • 前端 uni-app 初步使用指南
  • javaEE——单例模式
  • FreeRTOS菜鸟入门(十)·消息队列
  • MySQL用户管理
  • 以下是在 Ubuntu 上的几款PDF 阅读器,涵盖轻量级、功能丰富和特色工具:
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]: [1]如何加载动态链接库, 并无缝支持原生底层开发?