本附录梳理了 MySQL 面试中最常见的考点,按模块分类,每道题均附上核心答案要点。它不是简单的题目罗列,而是帮你理清面试官真正想考察的知识边界。
C.1 索引相关
1. B+ 树索引为什么是 InnoDB 的默认选择?与 B 树、哈希索引的区别是什么?
- 核心考点:磁盘 I/O 模型、范围查询性能、数据存储方式。
- 要点:
- B+ 树非叶子节点只存键,不存数据,一个节点能放更多键,树更矮,I/O 次数更少。
- 叶子节点形成有序链表,范围查询只需定位起点后顺序遍历,无需回溯。
- 哈希索引等值查询快,但不支持范围与排序;B 树非叶子节点存数据,范围查询需中序遍历,不友好。
- InnoDB 中数据即索引,主键索引的叶子节点存整行数据(聚簇索引)。
2. 聚簇索引与二级索引(辅助索引)的区别?
- 核心考点:数据存储结构、回表概念。
- 要点:
- 聚簇索引的叶子节点就是数据页,数据按主键顺序物理存储。一张表只能有一个聚簇索引。
- 二级索引的叶子节点存储的是主键值,如果需要查询的列不在二级索引中,需要拿主键值回到聚簇索引查完整行——称为“回表”。
- 因此主键不宜过大,否则二级索引会膨胀,且回表代价高。
3. 什么是联合索引的最左前缀原则?为什么会有索引失效?
- 核心考点:索引结构、查询条件顺序、常见反模式。
- 要点:
- 联合索引按定义时的列顺序建立 B+ 树。查询时从最左列开始匹配,不能跳过中间列(如
(a,b,c)索引,条件只有b和c则无法使用)。 - 范围查询(
>、<、BETWEEN)会打断后续列的索引匹配(但IN属于等值,不打断)。 - 失效常见原因:函数或计算(
WHERE YEAR(create_time)=2024)、隐式类型转换(字符串列给数字)、LIKE '%xx'、OR 连接非索引列、!=/NOT IN。 - 5.6 后的索引下推(ICP)可在引擎层过滤掉更多行,但需索引生效。
4. 覆盖索引是什么?如何用来优化查询?
- 核心考点:减少回表,性能优化手段。
- 要点:
- 如果 SQL 查询的列都被某个二级索引包含(不需回表),称为覆盖索引。
Extra中显示Using index。 - 适当建联合索引让高频查询走覆盖索引,大幅降低磁盘 I/O。
5. 索引如何选择?如何设计联合索引的列顺序?
- 核心考点:高选择性、业务查询模式。
- 要点:
- 选择性高的列(distinct 值多)优先放在联合索引前列。
- 将等值查询列放前面,范围查询列放最后。
- 结合业务 SQL 的具体 WHERE 条件与 ORDER BY 来建索引,尽量一个索引覆盖多个查询。
C.2 事务、锁与 MVCC
6. 事务的四大隔离级别分别是什么?InnoDB 是如何实现它们的?
- 核心考点:隔离级别、解决的问题(脏读、不可重复读、幻读)、MVCC 与锁的配合。
- 要点:
- 读未提交:可读到未提交数据,几乎不用。
- 读已提交:每个语句产生新 Read View,避免脏读,存在不可重复读和幻读。
- 可重复读(默认):事务内多次读取结果一致,Snapshot 读避免不可重复读,通过临键锁(Next-Key Lock)防止幻读。
- 串行化:所有读加共享锁,退化为串行执行,并发度最低。
- MVCC 实现:Undo Log 形成版本链,Read View 判断可见性,快照读不加锁。
7. 可重复读下如何避免幻读?
- 核心考点:临键锁(Next-Key Lock)机制。
- 要点:
- 对于锁定读(
SELECT ... FOR UPDATE或UPDATE/DELETE),在可重复读下 InnoDB 会使用间隙锁与记录锁组合成临键锁,锁住索引记录及其之间的间隙,防止其他事务插入。 - 纯快照读(普通 SELECT)不会加锁,不阻塞其他写入,所以也不能说有绝对幻读防护,除非你使用锁定读。
8. 死锁产生的条件?如何排查和解决?
- 核心考点:两阶段锁协议、互斥与循环等待、SHOW ENGINE INNODB STATUS、业务优化。
- 要点:
- 产生条件:互斥、持有并等待、不可剥夺、循环等待。MySQL 中常见于不同事务反向获取锁。
- 排查:
SHOW ENGINE INNODB STATUS中LATEST DETECTED DEADLOCK段落显示锁等待链。 - 解决:保证事务获取锁的顺序一致;缩短事务持有锁的时间;必要时调整隔离级别为读已提交(关闭间隙锁)。
9. 乐观锁和悲观锁的使用场景与实现方式?
- 核心考点:并发控制思路、CAS、版本号、SELECT FOR UPDATE。
- 要点:
- 悲观锁:假定冲突常发生,MySQL 中用
SELECT ... FOR UPDATE或LOCK IN SHARE MODE,事务内锁定数据行,并发度低但简单可靠。 - 乐观锁:假定冲突少,常用版本号或时间戳实现:
UPDATE ... WHERE version = oldVersion,更新时检查版本。 - 选择:冲突多(如秒杀库存)偏向悲观锁;冲突少(如用户资料更新)偏向乐观锁。也可结合 Redis 等缓存做预减。
C.3 SQL 优化与执行计划
10. EXPLAIN 各字段含义及如何判断执行计划好坏?
- 核心考点:分析性能瓶颈的基本功。
- 要点:
type:性能从好到差:system>const>eq_ref>ref>range>index>ALL。避免ALL全表扫描。key:实际使用的索引,若为 NULL 且rows很大,需要优化。rows:评估扫描行数,越小越好。Extra:关注Using filesort(额外排序)、Using temporary(临时表)、Using where、Using index(覆盖索引)。
11. 一条慢 SQL 的完整优化流程?
- 核心考点:系统性排查思路。
- 要点:
- 开启慢查询日志,设置阈值(
long_query_time),或在线SHOW PROCESSLIST捕捉。 EXPLAIN分析执行计划,确认是否走索引、扫描行数。- 检查索引设计:是否缺失、是否适合查询条件、是否存在索引失效。
- 考虑改写 SQL:拆分复杂子查询、用 UNION ALL 替代 UNION、避免 SELECT *。
- 考虑表结构或业务拆分:垂直/水平拆分、归档历史数据。
- 调整参数:如 Buffer Pool 大小等,但通常优化 SQL 优先于调参。
12. 深分页问题如何优化?
- 核心考点:
LIMIT offset, size的弊端与替代方案。 - 要点:
- 原因:
LIMIT 100000, 20需要扫描前 100020 行并丢弃前 100000 行,偏移量越大越慢。 - 解决方案:
- 基于索引的游标分页:
WHERE id > last_id ORDER BY id LIMIT 20。 - 先定位到 offset 对应的主键值,再借助主键回表:
SELECT * FROM t JOIN (SELECT id FROM t ORDER BY id LIMIT 100000,20) tmp ON t.id = tmp.id。 - 业务层面限制跳页深度,提供“上一页/下一页”。
13. 多表 JOIN 时驱动表的选择原则?
- 核心考点:优化器决策、小表驱动大表。
- 要点:
- 优化器一般选择数据量较小的表作为驱动表,Nested Loop Join 中驱动表扫描一次,被驱动表利用索引查找。
- 用
STRAIGHT_JOIN可以强制指定驱动表(谨慎使用)。 - 小表不单指行数,更指经过 WHERE 过滤后参与连接的行数少。
C.4 日志系统与数据恢复
14. Redo Log、Undo Log、Binlog 的作用与区别?
- 核心考点:持久性、原子性、复制与恢复基础。
- 要点:
- Redo Log:物理日志,记录数据页的修改,用于崩溃恢复,保证持久性。循环写,空间固定。
- Undo Log:逻辑日志,记录数据旧版本,用于事务回滚和 MVCC,保证原子性。在 undo 表空间中。
- Binlog:逻辑日志,记录 SQL 语句或行变更,用于主从复制和数据恢复。追加写,可自动切换文件。
15. 两阶段提交是什么?为什么需要它?
- 核心考点:Redo Log 与 Binlog 一致性保证。
- 要点:
- 流程:写入 Redo Log 并标记为 prepare → 写入 Binlog → 提交 Redo Log。
- 确保两者一致:崩溃恢复时,若发现 Redo 处于 prepare 且对应 Binlog 完整则提交,否则回滚。保证主从数据一致。
16. 如何基于时间点恢复误删数据?
- 核心考点:全量备份 + Binlog 回放。
- 要点:
- 前提:有定期全量备份和开启 Binlog,且 Binlog 保存时间足够。
- 步骤:还原最近一次全量备份 → 用
mysqlbinlog工具解析 Binlog,从备份时间点到误操作时间点之间的事件重新执行到数据库。 - 需注意:Binlog 格式为 ROW 或 MIXED 更可靠。
C.5 架构、复制与高可用
17. 主从复制的基本原理?延迟原因有哪些?
- 核心考点:I/O 线程、SQL 线程、Binlog。
- 要点:
- 主库将变更写入 Binlog,从库 I/O 线程拉取 Binlog 并写入 Relay Log,SQL 线程重放 Relay Log。
- 延迟原因:从库机器性能差;SQL 线程单线程重放(5.6 后支持并行复制);主库大事务;网络延迟。
18. 读写分离的实现方式及主备切换方案?
- 核心考点:ProxySQL、ShardingSphere、MHA、Orchestrator。
- 要点:
- 应用层手动路由;或通过 DB 代理中间件(ProxySQL、MaxScale)自动分发读写流量。
- 高可用切换:MHA(Master High Availability)自动检测主库故障并提升从库为新主;Orchestrator 提供可视化拓扑管理。
- MySQL 8.0 的组复制(MGR)提供原生的自动故障转移。
19. 分库分表常用策略与全局唯一 ID 生成方法?
- 核心考点:水平拆分、哈希一致性、雪花算法。
- 要点:
- 分片键选择:尽量让查询落到单分片,避免跨库 join。
- 全局 ID:雪花算法(Snowflake)生成趋势递增 64 位 ID;号段模式(如 Leaf);Redis incr;数据库自增表(性能差)。
- 跨库查询:通过中间件如 ShardingSphere 做聚合,或业务层代码装配。
C.6 存储引擎与内部原理
20. InnoDB 与 MyISAM 的主要区别?为什么生产必须用 InnoDB?
- 核心考点:事务、锁、崩溃恢复。
- 要点:
- InnoDB 支持事务和行级锁,MyISAM 不支持事务,只有表锁。
- InnoDB 崩溃后可依靠 Redo Log 自动恢复,MyISAM 崩溃后可能丢失数据。
- InnoDB 使用聚簇索引组织数据,MyISAM 数据文件和索引文件分离。
- 除非特殊用途(如只读 INSERT 日志),否则一律使用 InnoDB。
21. Buffer Pool 是如何提升性能的?LRU 淘汰算法有何改进?
- 核心考点:热数据缓存,预读与全表扫描防护。
- 要点:
- 缓存数据页和索引页,避免频繁磁盘 I/O。脏页定期刷盘。
- 改进 LRU:链表分 young 和 old 区,新数据先进入 old 区,若在 old 区再次被访问才移到 young 区,避免全表扫描冲掉热数据。
innodb_buffer_pool_size是最重要调优参数,一般设为内存的 60%~80%。
22. 表空间、段、区、页的层次关系?
- 核心考点:InnoDB 磁盘组织。
- 要点:
- 表空间(Tablespace) > 段(Segment,如数据段、索引段) > 区(Extent,1MB,64 个页) > 页(Page,16KB)。
- 默认独立表空间,每个表有自己的
.ibd文件,便于管理。
以上涵盖了大部分初中高级面试中的 MySQL 核心考点。实际面试时,很多问题会结合场景来问,建议不仅记住结论,更要理解背后的原理和设计权衡,做到讲得清,也用得上。