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

InnoDB如何解决脏读、不可重复读和幻读的?

InnoDB 引擎通过 MVCC(多版本并发控制)Next-Key Locking(临键锁) 两大核心机制解决脏读、不可重复读和幻读问题,具体实现原理如下:


一、解决脏读:MVCC 的 ReadView 机制

原理
事务只能读取已提交的数据版本(通过 Undo Log 构建历史版本)。
在这里插入图片描述

示例

  • 事务B修改数据(未提交) → 生成新版本(DB_TRX_ID = 100)
  • 事务A(ID=50)查询:
    • DB_TRX_ID=100 > 50 → 从Undo Log读取修改前的版本
    • 避免读取未提交的脏数据

隔离级别支持
READ COMMITTED(读已提交)及以上级别自动启用。


二、解决不可重复读:一致性快照(Consistent Read)

原理
在 REPEATABLE READ 级别,事务首次查询时创建 ReadView(快照),后续所有读取均基于此快照版本。
在这里插入图片描述

示例

  1. 事务A(ID=60)首次读取 balance=500
  2. 事务B(ID=70)修改 balance=800 并提交
  3. 事务A再次读取:
    • 检查DB_TRX_ID=70(不在事务A的ReadView活跃列表中)
    • 但事务A使用快照读 → 仍返回 balance=500

隔离级别支持
REPEATABLE READ(可重复读)级别生效。


三、解决幻读:Next-Key Locking(临键锁)

原理
MVCC 无法阻止其他事务插入新数据,因此 InnoDB 通过 临键锁 = 记录锁(Record Lock) + 间隙锁(Gap Lock) 锁定范围:

  • 记录锁:锁定索引记录
  • 间隙锁:锁定索引记录之间的范围(阻止插入)
    在这里插入图片描述

示例

-- 事务A:范围查询(加临键锁)
SELECT * FROM users WHERE age > 30 FOR UPDATE; 
-- 锁定现存age>30的记录 + 间隙(30, +∞)-- 事务B:尝试插入
INSERT INTO users (age) VALUES (35); -- 被阻塞!

隔离级别支持
REPEATABLE READ 级别自动启用临键锁。


四、InnoDB 解决方案总结表

问题解决机制技术实现触发条件
脏读MVCC 多版本读通过 Undo Log 读取已提交版本READ COMMITTED 及以上
不可重复读一致性快照(ReadView)事务内所有读操作基于首次快照REPEATABLE READ 级别
幻读Next-Key Locking记录锁 + 间隙锁锁定范围REPEATABLE READ + 写操作或显式锁

五、不同隔离级别的行为对比

操作READ COMMITTEDREPEATABLE READ
普通SELECT总是读最新已提交数据读事务开始时的快照
加锁SELECT(FOR UPDATE)仅加记录锁加临键锁(记录锁+间隙锁)
幻读风险可能发生完全避免

六、实战验证方案

1. 查看当前隔离级别
SELECT @@transaction_isolation; -- MySQL 8.0+
2. 测试不可重复读(REPEATABLE READ 下)
-- 事务A
START TRANSACTION;
SELECT balance FROM accounts WHERE id=1; -- 返回500-- 事务B(提交修改)
UPDATE accounts SET balance=800 WHERE id=1;
COMMIT;-- 事务A再次查询(仍返回500)
SELECT balance FROM accounts WHERE id=1; 
COMMIT;
3. 测试幻读防护
-- 事务A(加锁查询)
START TRANSACTION;
SELECT * FROM users WHERE age>30 FOR UPDATE; -- 锁住范围-- 事务B(尝试插入)
INSERT INTO users (name, age) VALUES ('Bob',35); -- 阻塞直到超时!

七、注意事项

  1. 写操作仍使用最新数据
    UPDATE/DELETE 总是基于最新提交数据(即使 REPEATABLE READ 级别)。

    -- 事务A
    SELECT * FROM accounts; -- 快照读:返回旧数据
    UPDATE accounts SET balance=balance+100; -- 更新基于最新数据!
    
  2. 显式加锁跳过 MVCC
    SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE 直接读取最新数据并加锁。

  3. 间隙锁的代价

    • 可能引发死锁(如两个事务互相等待对方间隙)
    • 可通过 innodb_locks_unsafe_for_binlog=ON 禁用(不推荐)

💡 最佳实践

  • 默认使用 REPEATABLE READ(InnoDB 的默认隔离级别)
  • 范围查询后立即操作数据时,显式加锁(FOR UPDATE
  • 写密集型场景监控锁竞争:SHOW ENGINE INNODB STATUS

InnoDB 通过 MVCC 和 Next-Key Locking 的精妙配合,在保证高并发的同时实现了数据强一致性,成为其作为事务型存储引擎的核心竞争力。

http://www.dtcms.com/a/328783.html

相关文章:

  • 天文与航天领域专业计算库介绍
  • C# 反射入门:如何获取 Type 对象?
  • Blender模拟结构光3D Scanner(一)外参数匹配
  • 决策树回归:用“分而治之”的智慧,搞定非线性回归难题(附3D可视化)
  • JS 与 C++ 双向通信实战:基于 WebHostViewListener 的消息处理机制
  • Java后端面试题(含Dubbo、MQ、分布式、并发、算法)
  • 分布式与微服务宝典
  • 智能算法流程图在临床工作中的编程视角系统分析
  • 【docker①】在VS Code中使用Docker容器
  • 安全点(Safepoint)完成后唤醒暂停线程的过程
  • 解决uni-app微信小程序编译报错:unexpected character `1`
  • 机器学习实战·第三章 分类(2)
  • EI学术会议 | 虚拟现实、图像和信号处理
  • 股指期货长线还是短线好?
  • AWS Redis Serverless连接完全指南:从安装到实战
  • Notepad++插件开发实战:从入门到精通
  • oss(阿里云)前端直传
  • 使用 Milvus Operator 在 Kubernetes 中部署 Milvus记录
  • LeetCode 刷题【40. 组合总和 II】
  • 3d游戏引擎中ContentTools中的文件模型导入代码1
  • python---list.sort() 和 sorted(list)的区别
  • JVM安全点轮询汇编函数解析
  • 计算机网络---IPv6
  • 第6节 torch.nn.Module
  • 熬夜面膜赛道跑出的新物种
  • Spring Boot初级概念及自动配置原理
  • 【递归、搜索与回溯算法】综合练习
  • 系统分析师-数据库系统-并发控制数据库安全
  • 使用 UDP 套接字实现客户端 - 服务器通信:完整指南
  • HiSmartPerf使用WIFI方式连接Android机显示当前设备0.0.0.0无法ping通!设备和电脑连接同一网络,将设备保持亮屏重新尝试