MySQL 面试
这一页只放面试标准回答和追问方向。详细原理要跳转到知识点页学习。
如果时间紧,先按知识地图与 7 天速学路线建立体系;完成本页重点题后,用 SQL 查询面试题与答案 训练现场编写 SQL,再通过复杂关联查询实战掌握多表结果正确性,最后用100 道高频面试题库随机口述,并用商业场景训练营补足项目追问。
MySQL 8 相比 5.7 新增了什么
标准回答:
SQL 层增加了 CTE、递归 CTE 和窗口函数;索引增加了降序索引、不可见索引、函数索引以及面向部分 JSON 数组查询的多值索引;优化器增加了直方图、EXPLAIN ANALYZE 和 Hash Join。底层使用 InnoDB 事务型数据字典替代 .frm,并增强了原子 DDL 和 Instant DDL。安全方面默认使用 utf8mb4 和 caching_sha2_password,并支持角色;工程能力还包括 CHECK 约束执行、NOWAIT、SKIP LOCKED、SET PERSIST、JSON_TABLE、Resource Group 和 Clone Plugin。同时移除了旧 Query Cache。升级时要检查驱动、认证插件、字符集和 collation、保留字、SQL mode、CHECK 约束、执行计划与数据字典,不能把切回旧镜像当作回滚。追问:
- 原子 DDL 与普通事务有什么区别?
- 不可见索引为什么仍然有写入成本?
EXPLAIN ANALYZE为什么不能随意在线上执行?- 5.7 升 8.x 为什么不能通过旧镜像降级?
- MySQL 8.4 为什么容易让旧客户端认证失败?
原理跳转:MySQL 8 新特性与 5.7 升级指南。
MySQL 怎么从零学到生产可用
标准回答:
MySQL 要按 SQL 基础、表设计、索引、执行计划、事务并发、日志恢复、主从复制和生产排查这条线学习。先掌握字段类型、主键、唯一约束和联合索引设计,再理解一条 SQL 会经过连接器、解析器、预处理器、优化器、执行器,最后由 InnoDB 通过 B+Tree、Buffer Pool、锁和 MVCC 访问数据。深入部分要理解聚簇索引、二级索引、回表、覆盖索引、EXPLAIN 估算行数、undo/redo/binlog、两阶段提交、主从复制和大表治理。生产排查要结合 EXPLAIN、慢日志、InnoDB 状态、锁等待、主从延迟和磁盘 IO。追问:
- 为什么有索引还会慢?
- undo、redo、binlog 分别解决什么?
- 主从延迟和大表深分页怎么处理?
原理跳转:从零到生产级掌握、从零到精通验收清单、SQL执行全过程原理、InnoDB更新事务全过程原理。
MySQL运行在Docker中要关注什么
标准回答:
MySQL容器化不能只会docker run。要理解官方Entrypoint只在空数据目录读取MYSQL_*变量、创建账号和执行init脚本;/var/lib/mysql必须使用有明确生命周期的Volume,但Volume不等于备份;配置、Secret、字符集、时区、网络和普通业务账号要外置并最小授权。资源上要把Buffer Pool、连接内存、临时表和其他全局结构统一纳入cgroup内存预算。备份要使用一致性逻辑或物理方案并恢复演练,升级要验证数据字典和磁盘格式,不能把镜像Tag切回当成数据库回滚。追问:
- 为什么修改MYSQL_ROOT_PASSWORD后旧密码仍生效?
- 为什么不能tar运行中的
/var/lib/mysql? - Buffer Pool没有占满容器为什么仍可能OOM?
- 为什么升级MySQL镜像后不能直接切回旧镜像?
原理跳转:MySQL容器化与数据安全、备份恢复、存储引擎与Buffer Pool、redo log与binlog。
MySQL、PostgreSQL、Oracle、SQL Server 核心区别
标准回答:
它们都属于关系型数据库,都支持 SQL、索引、事务、执行计划和恢复,但内部实现重点不同。MySQL InnoDB 常见于互联网业务,重点是 B+Tree、聚簇索引、Buffer Pool、undo、redo、binlog 和主从复制;PostgreSQL 更强调 SQL 标准、复杂查询、扩展索引、tuple 多版本、VACUUM 和 WAL;Oracle 常见于企业核心系统,重点是实例、SGA/PGA、Undo、Redo、SCN、AWR 和高可用;SQL Server 常见于微软生态,重点是聚集/非聚集索引、Transaction Log、锁、RCSI、TempDB 和参数嗅探。不能只背哪个更快,要看业务模型、事务一致性、报表分析、生态、成本和运维能力。追问:
- 为什么 MySQL 常说 B+Tree,而其他数据库常说 B-tree?
- redo、binlog、WAL、Transaction Log 分别解决什么?
- PostgreSQL 为什么需要 VACUUM?
- SQL Server 参数嗅探为什么会让 SQL 忽快忽慢?
原理跳转:主流关系库核心差异与选型、关系型数据库底层原理。
同一条 SQL 在不同数据库为什么性能不同
标准回答:
因为 SQL 文本只是逻辑请求,真正性能取决于存储组织、索引叶子层、统计信息、执行计划、可见性判断和回表方式。MySQL InnoDB 二级索引叶子层保存主键,查完整列可能回聚簇索引;PostgreSQL 索引通过 TID 找 heap tuple,还要做可见性判断;Oracle 索引通过 ROWID 回表,并基于 Undo + SCN 做一致性读;SQL Server 非聚集索引缺列时会 Key Lookup。所以“都走索引”不代表成本一样,还要看扫描范围、回表次数、排序、锁等待和缓存命中。追问:
- 为什么 MySQL 覆盖索引仍然可能慢?
- PostgreSQL Index Only Scan 为什么还可能访问 heap?
- Oracle ROWID 回表什么时候可能比全表扫描慢?
- SQL Server Key Lookup 多了怎么优化?
原理跳转:同一条列表查询在四种数据库里的差异、为什么走了索引还是慢。
MySQL 有几层
标准回答:
MySQL 可以分为连接层、Server 层和存储引擎层。连接层负责连接管理、认证和权限;Server 层负责 SQL 解析、预处理、优化器、执行器以及 binlog;存储引擎层负责真正的数据读写,例如 InnoDB 负责页、B+Tree、Buffer Pool、事务、锁、MVCC 和 redo log。这样分层后,MySQL 可以在 Server 层统一处理 SQL,在存储引擎层支持不同的数据存储实现。追问:
- binlog 和 redo log 分别属于哪一层?
- 优化器在哪一层?
- InnoDB 负责什么?
原理跳转:架构分层与请求全过程、SQL执行全过程原理、存储引擎与 Buffer Pool。
为什么 InnoDB 用 B+Tree
标准回答:
InnoDB 使用 B+Tree 是因为数据库索引要尽量减少磁盘 IO。B+Tree 的非叶子节点只保存 key 和页指针,一页能放更多目录项,树更矮;叶子节点保存真实数据或主键值,并且按 key 有序,适合范围查询、排序和分页;叶子节点之间有链表,找到范围起点后可以顺序扫描。普通二叉树树高太高,Hash 不支持范围和排序,所以 B+Tree 更适合磁盘页式存储。追问:
- B+Tree 和 B-Tree 区别?
- 为什么不用 Hash?
- Oracle、PostgreSQL、SQL Server 为什么文档说 B-Tree?
原理跳转:B+Tree、B-Tree 与数据库索引家族、MySQL 索引、InnoDB 存储结构。
聚簇索引和二级索引区别
标准回答:
InnoDB 的聚簇索引就是主键索引,叶子节点保存整行数据,所以一个表只能有一个聚簇索引。二级索引的叶子节点保存索引列和主键值,通过二级索引查完整行时,需要先找到主键,再回到聚簇索引查整行,这个过程叫回表。覆盖索引是查询字段都在二级索引中,不需要回表。原理跳转:B+Tree、B-Tree 与数据库索引家族、索引、存储结构。
EXPLAIN 的 rows 怎么来的
标准回答:
EXPLAIN 的 rows 不是实际扫描行数,而是优化器根据统计信息估算出来的预计扫描行数。统计信息包括表行数、索引基数、数据分布等。优化器会根据条件、索引和统计信息估算不同访问路径的成本,并选择成本较低的计划。如果统计信息不准,rows 也可能不准,执行计划可能选错,可以通过 analyze table 更新统计信息。EXPLAIN 估算和真实执行不一致怎么办
标准回答:
普通 EXPLAIN 是优化器执行前的预估,不代表真实扫描行数和真实耗时。如果怀疑估算不准,可以先看慢日志里的 Query_time、Rows_examined、Rows_sent,再在低风险环境用 EXPLAIN ANALYZE 对比 estimated rows 和 actual rows,用 optimizer_trace 看优化器为什么选择某个索引,也可以用 Handler_read% 指标辅助判断是索引范围扫还是全表扫。处理时不要只看 key 是否有值,要结合 rows、filtered、Extra、真实返回行数、锁等待、IO 和数据分布一起判断。追问:
EXPLAIN ANALYZE为什么生产要谨慎?Rows_examined和Rows_sent差距很大说明什么?optimizer_trace适合排查什么问题?
原理跳转:怎么验证 rows 估算是否靠谱、优化器原理。
慢 SQL 怎么从 EXPLAIN 推到优化方案
标准回答:
我会先明确 SQL 的业务访问模式,而不是直接加索引。比如一个列表页按 tenant_id、hospital_code、status 过滤,再按 created_at 排序分页,就要看 EXPLAIN 里 key 是否贴合这些条件,rows 是否过大,filtered 是否很低,Extra 是否有 Using filesort 或 Using temporary。如果只用了 tenant_id,status 索引,说明 hospital_code 还要额外过滤,created_at 还要额外排序。更合理的方向是设计 tenant_id,hospital_code,status,created_at 这样的联合索引,让等值条件先缩小范围,再利用索引顺序返回。优化后要用真实参数重新 EXPLAIN,对比 key、key_len、rows、filtered、Extra,再结合慢日志 Rows_examined、Rows_sent、P95/P99 和写入成本验证。追问:
- 为什么不能只看
key有值? - 为什么
Using filesort小数据量可以接受,大数据量要关注? - 为什么覆盖索引不能无脑把所有返回字段都放进去?
- 为什么新增索引后还要看写入成本和 Buffer Pool?
原理跳转:商业案例:资产列表慢 SQL 怎么从 EXPLAIN 推到优化方案、EXPLAIN、优化器原理。
事务 ACID
标准回答:
事务 ACID 指原子性、一致性、隔离性和持久性。原子性表示事务内操作要么都成功要么都失败,主要依赖 undo log 回滚;一致性表示事务前后数据满足约束和业务规则;隔离性表示并发事务之间互不干扰,依赖锁和 MVCC;持久性表示事务提交后即使宕机也能恢复,主要依赖 redo log。原理跳转:事务、MVCC、redo log 与 binlog、InnoDB更新事务全过程原理。
MVCC 和 undo log 的关系
标准回答:
MVCC 是多版本并发控制,目的是让普通 select 在不加锁的情况下读取一致性快照。InnoDB 每行有隐藏字段 trx_id 和 roll_pointer,roll_pointer 指向 undo log 中的旧版本。快照读会根据 Read View 判断当前版本是否可见,如果不可见,就沿 undo log 版本链查找可见旧版本。所以 undo log 不只用于回滚,也为 MVCC 提供历史版本。原理跳转:MVCC、InnoDB更新事务全过程原理。
redo log 和 binlog 区别
标准回答:
redo log 是 InnoDB 存储引擎层日志,主要用于崩溃恢复,保证事务提交后的修改不会因为宕机丢失;binlog 是 MySQL Server 层日志,记录逻辑变更,主要用于主从复制和时间点恢复。redo log 是循环写,binlog 通常追加写。事务提交时通过两阶段提交协调 redo log 和 binlog,避免崩溃后两份日志不一致。原理跳转:redo log 与 binlog、InnoDB更新事务全过程原理、主从复制。
为什么有索引还会慢
标准回答:
有索引只说明存在一种可能的访问路径,不代表扫描量小。MySQL 优化器会根据统计信息和成本选择是否使用索引;即使用了索引,也可能因为范围太大、回表太多、深分页、排序 filesort、临时表、返回列太多、Buffer Pool 命中率低或锁等待而慢。排查时要看 EXPLAIN 的 type、key、rows、filtered、Extra,再结合慢日志、实际返回行数和业务查询条件判断。原理跳转:从零到生产级掌握、EXPLAIN、大表覆盖索引仍然慢。
Join 为什么会慢,怎么优化
标准回答:
Join 慢的本质通常不是 Join 语法本身慢,而是执行过程中驱动表结果集太大、被驱动表 Join 字段没有索引、Join 顺序选择不合理、返回列过多、排序临时表过重,或者多表更新扩大锁范围。MySQL 常见 Join 可以理解为先从驱动表取行,再用 Join key 到被驱动表匹配。如果被驱动表能走索引,每次匹配成本较低;如果不能走索引,就可能变成驱动表每一行都触发被驱动表扫描。优化时先用 EXPLAIN 看驱动表、type、key、rows、Extra,再缩小驱动表结果集、给被驱动表 Join 字段建索引、调整联合索引顺序、避免大结果集深分页和一次 Join 过多大表。追问:
- 驱动表和 SQL 左表一定一样吗?
- 被驱动表 Join 字段没有索引会怎样?
- Block Nested Loop、BKA、Hash Join 分别解决什么问题?
left join的右表条件写在where和on有什么区别?- 多表
update join为什么容易造成锁等待和死锁?
为什么会死锁
标准回答:
死锁是两个或多个事务互相等待对方持有的锁,导致都无法继续。InnoDB 行锁是加在索引记录上的,范围查询还可能产生间隙锁和临键锁。常见原因包括多个事务更新顺序不一致、范围更新过大、索引设计不合理导致锁范围扩大。解决思路是保持更新顺序一致、缩小事务范围、让条件命中索引、降低锁范围,并根据死锁日志分析具体等待关系。原理跳转:锁机制。
主从延迟怎么处理
标准回答:
主从复制是主库写 binlog,从库拉取 binlog 写 relay log,再由 SQL 线程重放。主从延迟可能来自主库写入高峰、大事务、从库资源不足、慢查询或网络问题。处理方式包括拆小事务、提升从库规格、避免从库跑重查询、开启并行复制、强一致场景读主库、写后短时间读主库或根据延迟动态路由。原理跳转:主从复制。
单表数据量大,覆盖索引还是慢怎么办
标准回答:
覆盖索引只能减少回表,不代表扫描量小。如果单表数据量很大,即使走覆盖索引,也可能因为扫描范围大、深分页、排序归并、冷热数据、Buffer Pool 命中率低而慢。优化方向是缩小查询范围、改游标分页、调整联合索引顺序、做冷热归档、按业务拆表或分区,必要时再考虑分库分表。追问:
- 为什么
Using index不代表一定快? - 什么时候只改 SQL,什么时候归档,什么时候分库分表?
- 搜索和报表类需求为什么不一定适合 MySQL 扛?
分库分表什么时候做,怎么设计
标准回答:
分库分表是容量和吞吐治理手段,不是普通慢 SQL 的第一选择。应该先用 EXPLAIN、慢日志和真实数据证明瓶颈确实来自单表数据量、单库资源、写入吞吐或单机容量,而不是索引缺失、深分页、锁等待或历史数据未归档。设计时先确定核心访问路径和事务边界,再选择分片键,比如订单系统用户订单列表高频可以按 user_id,订单号查询高频则要有 order_no 到分片的路由表。分库分表后要补齐全局 ID、精准路由、跨分片查询限制、跨库事务方案、广播表/绑定表、扩容迁移、数据校验和回滚方案。追问:
- 为什么分库分表不是慢 SQL 的第一选择?
- 分片键选错会造成什么问题?
- 分库分表后分页排序为什么变难?
- 全局唯一 ID 有哪些方案?
- 扩容迁移为什么要双写、校验和回滚窗口?
原理跳转:分库分表、分片键是最核心的设计、扩容迁移怎么做。
