innodb 主要通过 mvcc(多版本并发控制) 和 next-key lock(临键锁) 机制来解决幻读问题,具体策略取决于事务隔离级别:
1.在可重复读(repeatable read)隔离级别下
mvcc 机制
- 通过 read view 机制实现快照读
- 事务开始时创建一致性视图,后续读取都基于这个视图
- 其他事务的插入操作不会影响当前事务的查询结果
next-key lock(临键锁)
- 记录锁(record lock):锁住索引记录
- 间隙锁(gap lock):锁住索引记录之间的间隙
- 临键锁 = 记录锁 + 间隙锁:锁住记录和前面的间隙
示例:
-- 假设表中有记录:10, 20, 30 select * from t where id > 15 for update; -- next-key lock 会锁住范围 (10, 20], (20, 30], (30, +∞] -- 阻止其他事务在这个范围内插入数据
2.解决幻读的场景分析
情况1:快照读(普通 select)
-- 事务a begin; select * from users where age > 20; -- 返回2条记录 -- 事务b insert into users (age) values (25); -- 成功,但不影响事务a -- 事务a再次查询 select * from users where age > 20; -- 仍然返回2条记录(无幻读)
原理:mvcc 保持一致性视图
情况2:当前读(加锁 select)
-- 事务a begin; select * from users where age > 20 for update; -- 加next-key lock -- 事务b尝试插入 insert into users (age) values (25); -- 被阻塞,等待锁释放
原理:next-key lock 阻止其他事务插入
3.不同隔离级别的对比
| 隔离级别 | 幻读解决方案 | 性能影响 |
|---|---|---|
| read committed | 可能发生幻读 | 低 |
| repeatable read | mvcc + next-key lock | 中等 |
| serializable | 完全串行化 | 高 |
4.实际应用建议
何时能完全避免幻读?
- 使用 repeatable read 隔离级别
- 查询使用索引(否则会锁全表)
- 当前读操作(for update、lock in share mode)
注意事项
-- 以下情况可能仍有幻读风险: -- 1. 混合使用快照读和当前读 begin; select * from t where id > 100; -- 快照读 update t set name = 'test' where id > 100; -- 当前读,可能看到新插入数据 -- 2. 使用 read committed 隔离级别 set session transaction isolation level read committed;
5.监控和调优
-- 查看锁信息 show engine innodb status; -- 监控锁等待 select * from information_schema.innodb_locks; select * from information_schema.innodb_lock_waits; -- 优化建议 -- 1. 合理设计索引,减少锁范围 -- 2. 避免长时间事务 -- 3. 必要时使用 serializable 隔离级别
总结
innodb 在 repeatable read 隔离级别下,通过 mvcc 解决快照读的幻读问题,通过 next-key lock 解决当前读的幻读问题。但需要注意事务中混合使用不同读取方式可能带来的不一致性。
到此这篇关于mysql中innodb幻读解决方案与机制的文章就介绍到这了,更多相关mysql innodb幻读内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论