隔离级别 → MVCC → 间隙锁 → 死锁 追问链#
追问路径#
Q: MySQL有哪几种事务隔离级别?分别解决什么问题?
→ RU(脏读)→RC(解决脏读)→RR(解决不可重复读,InnoDB默认)→Serializable(解决幻读,串行)
Q: InnoDB的RR级别是怎么实现的?靠什么避免不可重复读?
→ MVCC(快照读) + 锁(当前读)双管齐下;普通select走快照读,update/select for update走当前读加锁
Q: MVCC的快照是怎么实现的?
→ 每行隐藏trx_id+roll_pointer,undo log串成版本链;ReadView记录活跃事务,按可见性规则沿链找可见版本
├─ Q: RR级别完全解决幻读了吗?
│ → 快照读靠MVCC不会幻读;当前读靠间隙锁(Gap Lock)/Next-Key Lock锁住范围防止插入,基本解决但仍有边界case
│ Q: 间隙锁和Next-Key Lock是什么?
│ → 间隙锁锁两个索引记录之间的空隙(防插入);Next-Key=记录锁+间隙锁,锁住记录及其前面的间隙
│ Q: 行锁是加在数据上还是索引上?
│ → 加在索引上!没用到索引的查询会退化成表锁(锁全部记录),这是死锁和锁等待的常见原因
│ Q: RC和RR在加锁上有什么区别?
│ → RC没有间隙锁(只锁命中的行)并发更高、但有幻读;RR用Next-Key锁范围、一致性强但更易死锁
└─ Q: 死锁是怎么产生的?怎么排查和避免?
→ 两事务交叉持有并等待对方的锁;InnoDB有死锁检测会自动回滚代价小的事务
Q: 线上死锁怎么定位?
→ show engine innodb status看LATEST DETECTED DEADLOCK,或开innodb_print_all_deadlocks记录到错误日志
Q: 怎么从根上减少死锁?
→ 固定加锁顺序、缩小事务范围、走索引避免锁升级、热点行用乐观锁或拆分、降到RC并控制业务幂等plaintext涉及知识点#
- 事务隔离级别 — RU/RC/RR/Serializable与三类读问题
- MVCC实现原理 — ReadView+undo log版本链
- undo-log与事务回滚 — 版本链与回滚段
- 间隙锁与幻读 — Gap Lock/Next-Key Lock防幻读
- MySQL锁机制 — 共享/排他/意向锁/记录锁/间隙锁
- MySQL死锁排查 — 死锁检测与日志定位
- Buffer-Pool原理 — 锁与缓冲页的关系
- redo-log与binlog — 持久性与崩溃恢复
核心串联逻辑#
- 隔离级别递进:RU→RC→RR→Serializable,依次解决脏读、不可重复读、幻读,隔离性越强并发越低
- RR双机制:快照读用MVCC(ReadView+版本链)实现可重复读;当前读用Next-Key Lock锁范围防幻读
- MVCC可见性:ReadView记录生成时的活跃事务列表,沿undo log版本链找第一个对当前事务可见的版本(RC每次select生成新ReadView,RR只在首次)
- 锁加在索引上:未命中索引→表锁,这是锁等待和死锁的高发点;RC无间隙锁并发高但幻读,RR有间隙锁一致性强但易死锁
- 死锁治理:InnoDB自动检测回滚,根治靠固定加锁顺序+缩小事务+走索引
- 代码示例:
sql-- 排查最近一次死锁 SHOW ENGINE INNODB STATUS\G -- 看 LATEST DETECTED DEADLOCK 段 -- 当前读会加Next-Key Lock,RR下锁住(10,20]区间防插入 SELECT * FROM t WHERE id > 10 AND id <= 20 FOR UPDATE;
面试回答串联#
30秒速答#
“MySQL四个隔离级别RU/RC/RR/Serializable,InnoDB默认RR。RR靠MVCC的快照读避免不可重复读、靠间隙锁的当前读避免幻读。MVCC用每行的trx_id和undo log版本链加ReadView判断可见性。行锁加在索引上,没走索引会退化表锁。死锁是交叉持锁等待,InnoDB自动检测回滚,根治靠固定加锁顺序和缩小事务范围。“
2分钟展开答#
“MySQL有四个隔离级别:读未提交有脏读,读已提交解决脏读但有不可重复读,可重复读是InnoDB默认级别解决了不可重复读,串行化解决幻读但性能最差。InnoDB的RR靠两套机制:普通select是快照读走MVCC,update和select for update是当前读走加锁。MVCC的实现是每行有隐藏的trx_id和roll_pointer,历史版本通过undo log串成版本链,事务开启时生成ReadView记录当时活跃的事务列表,读的时候沿版本链找第一个对自己可见的版本——RC是每次select都生成新ReadView所以能读到别人已提交的,RR只在第一次select生成所以可重复读。RR下幻读的防范:快照读天然不会幻读,当前读靠间隙锁和Next-Key Lock锁住记录之间的空隙防止其他事务插入。这里关键点是行锁其实加在索引上,如果查询没命中索引会退化成锁全表,这是锁等待和死锁的常见根因。RC和RR的加锁区别在于RC没有间隙锁只锁命中行、并发高但有幻读,RR用Next-Key锁范围、一致性强但更容易死锁。死锁是两个事务交叉持有并互相等待对方的锁,InnoDB有死锁检测会自动回滚代价小的那个事务。线上定位用show engine innodb status看最近一次死锁详情,或者开innodb_print_all_deadlocks记到错误日志。根治上我会固定加锁顺序、尽量缩小事务范围、确保走索引避免锁升级、热点行用乐观锁。“
相关追问链#
- MySQL索引-慢SQL-优化实战追问链 — 索引是行锁的载体、覆盖索引减少加锁
- MySQL事务-分布式事务-CAP追问链 — 从单机ACID到分布式事务
- Redis缓存问题-一致性-分布式锁追问链 — 缓存与DB一致性、乐观锁防超卖
- 分库分表-分布式ID-读写分离追问链 — 分库后跨库事务与一致性