一、MySQL为什么使用B+树而不是B树?
✅ 面试八股文回答:
MySQL使用B+树作为索引结构,主要是为了提高磁盘IO性能。相比B树,B+树具有以下优势:
所有的值都在叶子节点,非叶子节点只存储键,不存储数据,单个节点能容纳更多索引项,提高扇出(fan-out),树的高度更小,查询更快。
叶子节点形成一个有序链表,适合范围查询或排序操作,只需顺序遍历叶子节点即可,效率更高。
B+树在每次查找时都从根节点走到叶子节点,查询路径统一、稳定,有利于磁盘预读和缓存命中。
🎯 场景题:
你有一张百万级别的大表,需要频繁按某个字段范围查询,应该选择哪种索引结构?为什么?
👉 回答思路:使用B+树索引,因为它对范围查询友好,叶子节点有序链表结构可以顺序扫描。
二、MySQL加锁机制:有索引 vs 无索引 加什么锁?
✅ 面试八股文回答:
MySQL(InnoDB引擎)中,对于 SELECT ... FOR UPDATE
或 UPDATE/DELETE
等加锁操作:
有索引并且命中索引:加的是行锁(Record Lock),只锁住匹配到的行,效率高,支持高并发。
无索引或索引未命中:加的是表锁或间隙锁(Gap Lock)/临键锁(Next-Key Lock),因为需要扫描整张表,锁粒度大,可能导致阻塞。
特别注意:如果使用了范围条件且未命中索引,InnoDB会退化为间隙锁 + 行锁(Next-Key Lock),甚至是表锁,导致并发性能下降。
🎯 场景题:
你在用 UPDATE users SET age=30 WHERE name='张三'
,name字段没有加索引,会发生什么?
👉 回答思路:
因为没有索引,InnoDB需要扫描整表来查找name='张三'的记录;
因此会加表锁或对所有行加行锁,导致并发性能下降甚至出现死锁;
解决方式是给name字段加索引,减少锁粒度。
三、MySQL事务四大特性(ACID)与隔离级别
✅ 面试八股文回答:
A:原子性(Atomicity)——事务中所有操作要么全部成功要么全部失败。
C:一致性(Consistency)——事务执行前后,数据都保持一致。
I:隔离性(Isolation)——多个事务之间互不干扰。
D:持久性(Durability)——事务提交后数据永久保存。
InnoDB默认隔离级别是:可重复读(Repeatable Read),通过MVCC机制解决幻读问题。
🎯 场景题:
为什么MySQL默认使用可重复读而不是串行化隔离级别?
👉 回答思路:
可重复读可以避免大部分并发问题(不可重复读、幻读),但又比串行化隔离级别效率高。
串行化虽然最安全但会严重降低并发性能。
所以InnoDB通过MVCC机制,在可重复读下实现了高并发和一致性的平衡。
四、MySQL常见慢SQL优化分析步骤?
✅ 面试八股文回答:
慢SQL优化步骤包括:
使用
EXPLAIN
分析执行计划;检查是否使用索引,是否走了全表扫描;
评估数据量是否合理,是否有冗余或重复查询;
是否可以使用覆盖索引(索引包含查询字段);
使用
慢查询日志
和SHOW PROFILE
分析执行时间;考虑分库分表或缓存。
🎯 场景题:
你发现一个接口经常超时,SQL语句比较简单,但表很大,如何排查?
👉 回答思路:
先用
EXPLAIN
看是否走索引;如果没走索引,分析原因(字段是否索引、条件写法问题如函数包裹);
用
SHOW INDEX
看索引情况;检查是否能使用覆盖索引;
最后考虑加缓存、分表或归档历史数据。
五、聚簇索引 vs 非聚簇索引
✅ 面试八股文回答:
InnoDB的主键索引是聚簇索引,数据和索引存储在一起;
辅助索引是非聚簇索引,只存储主键值,再通过主键定位数据(回表);
所以查询效率:主键查询 > 覆盖索引 > 辅助索引(需要回表);
如果有多个查询条件优先考虑使用主键或覆盖索引,避免回表。
🎯 场景题:
一张表经常根据 email
查询,这个字段有唯一约束,你该怎么设计索引?
👉 回答思路:
如果email唯一,考虑将其设为主键,作为聚簇索引;
否则,加唯一索引,并尽量让查询字段出现在索引中,构建覆盖索引避免回表。
📌 总结:MySQL八股点清单(用于突击面试)
知识点 | 面试高频问题 | 建议准备方式 |
---|---|---|
索引 | 为什么用B+树?聚簇索引和非聚簇区别?回表是什么? | 图解 + 口头复述 |
锁机制 | 行锁/表锁怎么加?有索引与无索引加什么锁?死锁怎么排查? | 举例回答,推荐结合 SHOW ENGINE INNODB STATUS |
执行计划 | EXPLAIN 中各字段含义?type=ALL是好的吗? |
记住type从好到坏的顺序 |
事务 | ACID是什么?四种隔离级别?MySQL默认哪个? | 对比幻读/脏读/不可重复读 |
MVCC | 是什么?怎么实现?为什么默认可重复读还能避免幻读? | 讲undo log和隐藏版本号 |
SQL优化 | 慢SQL怎么查?索引怎么建?什么时候用覆盖索引? | 多练 EXPLAIN |
索引失效 | 哪些写法会导致索引失效?(如函数包裹、!=) | 举例练习 |
count(*)优化 | 为什么 count(*) 这么快? | 了解聚簇索引原理 |
✅ MySQL高频面试问题总结性答案(建议背诵)
1. 为什么MySQL索引使用 B+ 树而不是 B 树?
答:
MySQL使用B+树作为索引,是为了提高查询效率:
B+树非叶子节点不存储数据,单节点能容纳更多键值,扇出高、树高度低,磁盘IO少;
所有数据只存在叶子节点,叶子节点之间通过链表连接,支持范围查询效率更高;
查询必须走到叶子节点,路径固定,更利于缓存与预读优化。
2. 有索引和无索引时,加的锁有什么区别?
答:
InnoDB默认使用行级锁;
如果 有索引且命中索引:加的是行锁;
如果 无索引或索引没命中:可能加表锁或大范围的间隙锁(Gap Lock),影响性能;
在范围查询下,还可能加 Next-Key Lock(间隙锁 + 行锁)。
3. InnoDB 为什么默认隔离级别是可重复读?
答:
可重复读能解决不可重复读问题,还通过MVCC解决幻读问题;
比串行化性能高,比读已提交更安全;
兼顾数据一致性和并发性能,是InnoDB中最实用的隔离级别。
4. MySQL 行锁、表锁、间隙锁分别是什么?
答:
行锁(Record Lock):锁住某一行数据,粒度小,效率高;
表锁:锁住整张表,其他线程不能对其做任何修改操作;
间隙锁(Gap Lock):锁住某个索引值之间的区间,防止幻读;
Next-Key Lock:是行锁和间隙锁的组合,锁住"记录+区间"。
5. MVCC 是怎么实现的?
答:
MVCC通过以下两部分实现:
Undo Log:记录数据修改前的版本;
隐藏字段:trx_id, roll_pointer;
查询时只读取小于当前事务版本号的数据,读写分离,无需加锁。
适用于 可重复读、读已提交 隔离级别,避免加锁,提高并发。
6. EXPLAIN 中 type 字段各个值代表什么?
答(从好到差):
system
>const
>eq_ref
>ref
>range
>index
>ALL
;ALL
:全表扫描,性能最差;ref
/range
:表示使用了索引;优化目标是让 type 尽量在
ref
或更优级别。
7. 什么是覆盖索引?怎么优化查询用它?
答:
覆盖索引:查询只访问索引,不需要回表;
索引字段包含了所有需要的字段;
优化方式:将查询字段设计为联合索引的一部分,比如:
index(name, age)
查询select age where name=...
。
8. 回表操作是什么?为什么会影响性能?
答:
InnoDB的辅助索引不存储整行数据,只存主键;
查询非索引字段时,需要通过主键再去聚簇索引里查一次,叫做回表;
回表是额外的I/O开销,多一次查找,性能变差。
9. 哪些情况会导致索引失效?
答:
使用了
or
;使用函数包裹字段,如
WHERE DATE(create_time)
;索引字段有
!=
、<>
;隐式类型转换,如
WHERE id = '123'
;联合索引不满足最左前缀原则。
10. count(*)为什么很快?
答:
InnoDB会通过聚簇索引的页结构统计行数,不会实际读取每一行;
对于MyISAM引擎,表有专门的计数器;
但是加了WHERE条件,count性能会变差,因为必须扫描数据匹配。