复习定位
MySQL 面试最常问的是 InnoDB。回答时优先把索引、事务、锁、日志和执行计划连起来,不要只背零散概念。
InnoDB 和索引
为什么常说 InnoDB 用 B+ 树?
B+ 树适合磁盘存储:
- 非叶子节点只存键和指针,单页能放更多索引项,树高更低。
- 叶子节点按顺序连接,范围查询方便。
- 每次查询路径比较稳定,适合数据库页读取。
聚簇索引和二级索引
InnoDB 的主键索引是聚簇索引,叶子节点存整行数据。
二级索引的叶子节点存主键值。通过二级索引查到主键后,如果还需要其他列,就要回表到聚簇索引查整行。
优化方向:能用覆盖索引就减少回表。
最左前缀原则
联合索引按从左到右顺序生效。比如索引 (a, b, c):
where a = ?可以用。where a = ? and b = ?可以用。where b = ?通常用不上这个联合索引的完整优势。- 遇到范围查询后,后面的列可能无法继续用于有序定位。
索引失效常见情况
常见原因:
- 对索引列做函数或表达式计算。
- 字符串不加引号导致隐式转换。
- 左模糊查询,例如
like '%abc'。 - 联合索引不满足最左前缀。
- 数据量太小或优化器判断全表更划算。
事务和隔离级别
ACID:
- Atomicity:原子性,要么都成功,要么都失败。
- Consistency:一致性,事务前后约束不被破坏。
- Isolation:隔离性,并发事务之间互不干扰到指定程度。
- Durability:持久性,提交后数据可靠保存。
隔离级别:
- READ UNCOMMITTED:可能脏读。
- READ COMMITTED:解决脏读,但可能不可重复读。
- REPEATABLE READ:MySQL InnoDB 默认级别,配合 MVCC 和锁解决大量并发问题。
- SERIALIZABLE:最严格,并发性能最低。
MVCC
MVCC 是多版本并发控制,让读写尽量不互相阻塞。
核心概念:
- undo log 保存历史版本。
- Read View 决定当前事务能看到哪些版本。
- 隐藏字段记录事务 id 和回滚指针。
快照读走 MVCC,例如普通 select。当前读读取最新数据并加锁,例如 select ... for update、update、delete。
锁
常见锁:
- 行锁:锁记录,粒度小。
- 表锁:锁整张表,粒度大。
- 间隙锁:锁索引记录之间的间隙,防止幻读。
- Next-Key Lock:记录锁 + 间隙锁。
面试重点:InnoDB 行锁是基于索引实现的。如果条件没走索引,可能退化成更大范围的锁。
日志
redo log
redo log 保证崩溃恢复。事务提交后,即使脏页还没刷到磁盘,也能通过 redo log 恢复。
undo log
undo log 用于回滚和 MVCC 历史版本读取。
binlog
binlog 是 MySQL Server 层日志,常用于主从复制和数据恢复。
常见追问:两阶段提交用于协调 redo log 和 binlog,降低崩溃时两份日志不一致的风险。
慢查询优化思路
可以按顺序排查:
- 看 SQL 是否只查必要列,避免
select *。 - 用 explain 看访问类型、索引、扫描行数。
- 检查 where、order by、group by 是否能利用索引。
- 避免大偏移分页,必要时用基于游标的翻页。
- 控制单表数据量,必要时归档或分库分表。
- 结合业务缓存,但不要把所有问题都丢给缓存。
面试收束
MySQL 高频题可以用一条线串:查询为什么慢 -> 索引怎么建 -> 事务怎么隔离 -> 锁怎么影响并发 -> 日志怎么保证可靠。
参考资料
- JavaGuide:https://javaguide.cn/
- JavaGuide GitHub:https://github.com/Snailclimb/JavaGuide