InnoDB默认是用可重复读,那发生幻读怎么办?

举报
一颗小谷粒 发表于 2025/07/31 19:05:25 2025/07/31
【摘要】 InnoDB默认是用可重复读,那发生幻读怎么办?InnoDB 的默认隔离级别是「可重复读(Repeatable Read, RR)」,在该级别下,幻读问题并未被彻底解决,但通过 MVCC(多版本并发控制)和锁机制(Next-Key Lock)的组合,可以大幅减少幻读的发生。以下是详细分析及案例:一、InnoDB 解决幻读的机制1. 快照读:通过 MVCC 避免幻读原理事务启动时会生成一个一致...

InnoDB默认是用可重复读,那发生幻读怎么办?


InnoDB 的默认隔离级别是「可重复读(Repeatable Read, RR)」,在该级别下,幻读问题并未被彻底解决,但通过 MVCC(多版本并发控制)和锁机制(Next-Key Lock)的组合,可以大幅减少幻读的发生。以下是详细分析及案例:


一、InnoDB 解决幻读的机制

1. 快照读:通过 MVCC 避免幻读

  • 原理
    事务启动时会生成一个一致性视图(Read View),后续所有普通 SELECT 查询(快照读)均基于该视图读取数据。即使其他事务插入新数据并提交,当前事务也无法看到这些新增记录,从而避免幻读。
  • 示例

    -- 事务 A
    BEGIN;
    SELECT * FROM t WHERE id > 2;-- 快照读,生成 Read View,返回结果:小红、小蓝
    -- 事务 B 插入 id=5 并提交
    SELECT * FROM t WHERE id > 2;-- 仍返回小红、小蓝,不受事务 B 影响
    COMMIT;
    

2. 当前读:通过 Next-Key Lock 避免幻读

  • 原理
    当执行 SELECT ... FOR UPDATEUPDATEDELETE 等需要“当前读”的操作时,InnoDB 会使用 Next-Key Lock(记录锁 + 间隙锁),锁定索引范围,阻止其他事务在该范围内插入数据。
  • 示例

    -- 事务 A
    BEGIN;
    SELECT * FROM  tWHERE id > 2 FORUPDATE;-- 锁定 (2, +∞),阻止插入
    -- 事务 B 尝试插入 id=5 时会被阻塞
    COMMIT;
    

二、幻读仍可能发生的场景

1. 唯一索引 + 等值查询无匹配记录

  • 问题
    当查询条件为唯一索引的等值查询且无匹配记录时,InnoDB 可能不会加间隙锁,导致其他事务可插入新数据。
  • 案例

    -- 事务 A
    BEGIN;
    SELECT * FROM t WHERE id = 10 FORUPDATE;-- 表为空,未加间隙锁
    -- 事务 B 插入 id=10 并提交
    SELECT * FROM t WHERE id = 10 FORUPDATE;-- 出现新记录,导致幻读
    COMMIT;
    

2. 未命中索引的查询

  • 问题
    若查询未使用索引,InnoDB 会退化为 表级锁,但无法阻止其他事务在间隙中插入数据。
  • 案例: 
    sql -- 表 t 无索引 -- 事务 A BEGIN; SELECT * FROM t WHERE value = 'test' FOR UPDATE; -- 全表扫描,加表锁 -- 事务 B 插入 value='test' 的新记录时可能被阻塞,但具体取决于锁机制是否覆盖所有间隙

3. 混合操作引发隐式幻读

  • 问题
    若事务中先快照读再执行当前读操作(如 UPDATE),可能导致隐式读取到其他事务插入的数据。
  • 案例

    -- 事务 A
    BEGIN;
    SELECT * FROM t WHERE teacher_id = 1;-- 快照读,返回空
    -- 事务 B 插入 teacher_id=1 的数据并提交
    UPDATE t SET name = 'wang' WHERE teacher_id = 1;-- 当前读,更新事务 B 插入的数据
    SELECT * FROM t WHERE teacher_id = 1;-- 出现新数据,导致幻读
    COMMIT;
    

三、彻底解决幻读的方案

  1. 升级隔离级别至串行化(Serializable)

    • 通过表级锁完全禁止并发写入,但会显著降低性能。
  2. 合理使用索引和锁

    • 对范围查询显式加锁(如 FOR UPDATE),确保 Next-Key Lock 生效。
    • 为查询条件添加索引,避免锁退化。
  3. 业务层控制

    • 在事务中尽量减少混合操作(快照读 + 当前读),或通过乐观锁(如版本号)校验数据一致性。

四、总结

  • InnoDB 的 RR 隔离级别
    通过 MVCC 和 Next-Key Lock 的组合,在大多数场景下可避免幻读,但特定条件下(如无索引、唯一索引等值查询无记录)仍存在风险。
  • 开发建议
    根据业务需求选择隔离级别,对关键操作显式加锁并合理设计索引,必要时通过业务逻辑补充校验。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。