当前位置: 首页 > 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.xdnf.cn/news/1294111.html

相关文章:

  • 公司项目用户密码加密方案推荐(兼顾安全、可靠与通用性)
  • HiSmartPerf使用WIFI方式连接Android机显示当前设备0.0.0.0无法ping通!设备和电脑连接同一网络,将设备保持亮屏重新尝试
  • antdv Modal的简单使用
  • Hive 创建事务表的方法
  • 18. parseInt 的参数有几个
  • 开源工具can-utils的使用
  • Docker 在 Linux 中的额外资源占用分析
  • 云计算-实战 OpenStack 私有云运维:服务部署、安全加固、性能优化、从服务部署到性能调优(含数据库、内核、组件优化)全流程
  • OpenCV常见问题汇总
  • Java JDK8环境配置
  • 基于Java的Markdown转Word工具(标题、段落、表格、Echarts图等)
  • 药房智能盘库系统:基于CV与时间序列预测的库存革命
  • 《算法导论》第 22 章 - 基本的图算法
  • Kubernetes-03:Service
  • Notepad++插件开发实战
  • Mac安装ant
  • [系统架构设计师]系统架构基础知识(一)
  • Flutter权限管理三步曲:检查、申请、处理全攻略
  • 三、非线性规划
  • 第十二节:粒子系统:海量点渲染
  • nm命令和nm -D命令参数
  • Docker部署 Neo4j 及集成 APOC 插件:安装与配置完整指南(docker-compose)
  • python的游戏评级论坛系统
  • [AI React Web] 包与依赖管理 | `axios`库 | `framer-motion`库
  • 《探索C++ set与multiset容器:深入有序唯一性集合的实现与应用》
  • 实盘回测一体的期货策略开发:tqsdk获取历史数据并回测,附python代码
  • Java 基础概念笔记
  • davici configurator 报错:License file of SIP has no valid checksum.
  • GitHub宕机时的协作方案
  • 如何使用 Ollama 在本地设置并运行 Qwen3