MySQLNotes

第 34 章:面试题精讲

zjc 于 2026-02-03 发布

这是《MySQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 MySQL 面试通常不是考背诵,而是从一道基础题开始不断追问,直到暴露你对索引、事务、日志、复制和故障处理的理解边界。本章把高频问题整理成“问题、核心答案、追问、易错点”的形式,帮助你把前 33 章的知识转成自己的表达。

34.1 答题框架

回答技术问题建议使用四步:

1. 定义:它是什么
2. 原理:为什么这样工作
3. 场景:什么时候有效,什么时候失效
4. 实践:生产中怎么验证和治理

例如被问到索引为什么快,不要只回答“B+Tree”。更好的回答是:

InnoDB 主键是聚簇 B+Tree,数据按主键顺序存放;
查询从根节点到叶子节点,访问次数与树高相关;
二级索引叶子保存主键,必要时回表;
索引能减少扫描,但写入和维护有代价;
生产上要看执行计划、扫描行数、回表和排序成本。

这种回答既展示知识,也展示工程判断。

34.2 基础与架构

问题 1:一条 SELECT 语句的执行流程是什么?

客户端连接
  -> 连接器认证与权限检查
  -> 解析器生成语法树
  -> 预处理做语义检查
  -> 优化器选择执行计划
  -> 执行器调用存储引擎接口
  -> InnoDB 读取 Buffer Pool 或磁盘页
  -> 返回结果集

常见追问:

  1. 权限在哪里检查?连接时会做全局权限检查,语句执行前还会按对象检查;
  2. Server 层和存储引擎层怎么分工?Server 层负责解析、优化和执行流程,InnoDB 负责数据、事务、锁、MVCC 和持久化;
  3. MySQL 8.0 为什么移除 Query Cache?命中率不稳定,锁竞争和失效代价高,收益有限。

问题 2:InnoDB 和 MyISAM 有什么区别?

维度 InnoDB MyISAM
事务 支持 不支持
锁粒度 行级锁,并有表级意向锁等 表锁
外键 支持 不支持
崩溃恢复 Redo / Undo 支持 依赖修复工具
MVCC 支持 不支持
存储组织 主键聚簇 B+Tree 索引和数据分离
现代默认 MySQL 5.5 后默认 不建议新业务使用

加分点:不要说 InnoDB “只有行锁没有表锁”。DDL、AUTO-INC 锁、意向锁和元数据锁仍可能影响整表。

问题 3:为什么新表使用 utf8mb4?

历史 utf8 实际是最长三字节的 utf8mb3,无法完整保存 Emoji、部分生僻字和补充字符;utf8mb4 才是完整 UTF-8 编码。

实践建议:

  1. 新表统一 utf8mb4
  2. 显式指定排序规则;
  3. 关注排序规则混用导致的隐式转换;
  4. 老表迁移前评估索引长度和存储变化。

34.3 索引与执行计划

问题 4:为什么 InnoDB 选择 B+Tree?

B+Tree 的特点:

  1. 非叶子节点只保存键和指针;
  2. 叶子节点保存数据或主键;
  3. 叶子节点形成链表;
  4. 等值和范围查询都友好;
  5. 树高低,减少磁盘页访问次数。

哈希索引适合等值查询,但不适合范围查询、排序和最左前缀匹配。InnoDB 的自适应哈希索引是内部机制,不是用户手工创建的普通哈希索引。

问题 5:什么是回表、覆盖索引和最左前缀?

示例:

CREATE INDEX idx_user_status_created
ON orders (user_id, status, created_at);

SELECT order_no, amount
FROM orders
WHERE user_id = 10001
  AND status = 'PAID';

回答:

  1. 二级索引叶子节点保存 user_idstatuscreated_at 和主键;
  2. 查询还需要的 order_noamount 不在索引中,需要用主键回表;
  3. 如果查询只选择索引列和主键,就是覆盖索引;
  4. 联合索引遵循最左前缀原则;
  5. 单独按 status 查询无法完整使用该索引。

追问:以下条件分别怎么使用索引?

WHERE user_id = 1 AND status = 'PAID';
WHERE user_id = 1 AND created_at > '2026-08-01';
WHERE status = 'PAID' AND created_at > '2026-08-01';
WHERE user_id > 100;

前三个可能使用索引的不同部分,最后一个只能按 user_id 做范围访问。最终以执行计划和统计信息为准。

问题 6:EXPLAIN 最应该看什么?

优先看:

  1. typeconsteq_refrefrangeindexALL 等;
  2. keypossible_keys
  3. rows 预估扫描行数;
  4. filtered 过滤比例;
  5. Extra 是否出现 Using filesortUsing temporaryUsing index
  6. idselect_type,判断多表和子查询顺序。

MySQL 8.0 可以使用:

EXPLAIN ANALYZE
SELECT order_no, amount
FROM orders
WHERE user_id = 10001
  AND status = 'PAID';

EXPLAIN ANALYZE 会真实执行并展示每步耗时和行数,适合验证优化效果,但不要在高峰对大查询随意执行。

问题 7:索引失效的常见原因?

常见原因:

  1. 对索引列做函数或表达式计算;
  2. 隐式类型转换;
  3. 隐式字符集或排序规则转换;
  4. 前导模糊匹配;
  5. 不满足联合索引最左前缀;
  6. OR 连接了无索引条件;
  7. 优化器认为全表扫描代价更低;
  8. 统计信息过期;
  9. 分区裁剪失效;
  10. 索引被隐藏。

示例改写:

-- 可能无法使用 created_at 索引
WHERE DATE(created_at) = '2026-08-01';

-- 更容易使用范围索引
WHERE created_at >= '2026-08-01 00:00:00'
  AND created_at <  '2026-08-02 00:00:00';

不要把规则绝对化。不同版本和写法下优化器行为可能不同,最终必须看执行计划。

34.4 事务、MVCC 与锁

问题 8:ACID 分别靠什么实现?

特性 含义 主要机制
Atomicity 全部成功或全部回滚 Undo Log
Consistency 数据满足约束和业务规则 事务、约束、锁、应用逻辑
Isolation 并发事务互不异常干扰 MVCC、锁、隔离级别
Durability 提交后不丢 Redo Log、Binlog、Doublewrite

不要说一致性只靠数据库。数据库能保证主键、唯一、外键和 CHECK 等约束,业务不变量仍需要正确的事务边界。

问题 9:四个隔离级别分别解决什么问题?

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED 避免 可能 可能
REPEATABLE READ 避免 避免 InnoDB 在当前读下通过 Next-Key Lock 避免大部分幻读
SERIALIZABLE 避免 避免 避免

InnoDB 默认是 REPEATABLE READ。很多其他数据库默认 READ COMMITTED,不要混淆。

追问为什么互联网业务常用 RC:RC 通常锁冲突更少,更容易支撑高并发;代价是同一事务内多次快照读可能看到不同版本,需要业务接受这个语义。

问题 10:MVCC 怎么工作?

核心要素:

  1. 行隐藏字段:DB_TRX_IDDB_ROLL_PTR
  2. Undo Log 版本链;
  3. ReadView;
  4. 活跃事务列表;
  5. ReadView 创建时机。

可见性判断可以概括为:

版本事务 ID 小于 ReadView 低水位:可见
版本事务 ID 大于高水位:不可见
版本事务仍在活跃列表中:不可见
版本事务不在活跃列表且已提交:可见

RR 在事务第一次快照读时创建 ReadView 并复用;RC 每次快照读都创建新的 ReadView。

问题 11:当前读和快照读的区别?

快照读:

SELECT * FROM orders WHERE id = 1;

当前读:

SELECT * FROM orders WHERE id = 1 FOR UPDATE;
SELECT * FROM orders WHERE id = 1 LOCK IN SHARE MODE;
UPDATE orders SET status = 'PAID' WHERE id = 1;
DELETE FROM orders WHERE id = 1;
INSERT INTO orders (...);

快照读走 MVCC;当前读读取最新版本并加锁。讨论 RR 下的幻读,必须先区分这两种读。

问题 12:行锁、间隙锁和 Next-Key Lock 是什么?

Record Lock:锁索引记录
Gap Lock:锁索引记录之间的间隙,不包含记录本身
Next-Key Lock:Record Lock + Gap Lock
Insert Intention Lock:插入前表达插入意图

示例:

SELECT * FROM users
WHERE age BETWEEN 20 AND 30
FOR UPDATE;

RR 下可能锁住满足条件的记录和相关间隙,阻止其他事务插入导致幻读的新行。如果 age 没有索引,锁范围可能扩大,这是必须强调的易错点。

问题 13:死锁怎么排查和预防?

排查命令:

SHOW ENGINE INNODB STATUS\G
SELECT * FROM sys.innodb_lock_waits;
SELECT * FROM performance_schema.data_locks;

要看四件事:

  1. 两个事务分别执行了什么;
  2. 持有和等待的锁;
  3. 加锁顺序;
  4. 是否存在无索引条件、唯一冲突、间隙锁或大事务。

预防:

  1. 缩短事务;
  2. 按相同顺序访问表和行;
  3. 为过滤条件建立合适索引;
  4. 避免在事务中等待外部调用;
  5. 减少热点行竞争;
  6. 使用合理重试策略;
  7. 必要时评估隔离级别和锁范围。

34.5 日志、复制与高可用

问题 14:Redo、Undo、Binlog 的区别?

日志 层级 作用 特点
Redo Log InnoDB 崩溃恢复,保证持久性 物理日志,顺序写
Undo Log InnoDB 回滚和 MVCC 版本链 逻辑反操作
Binlog Server 复制、恢复、CDC 逻辑日志,追加写

两阶段提交简化流程:

1. InnoDB 写 Redo Log,进入 prepare
2. 写 Binlog
3. InnoDB 将事务置为 commit

如果 Redo 已 prepare 而 Binlog 未成功,崩溃恢复时回滚;如果 Binlog 完整,则提交。这样避免主库、Binlog 和从库数据不一致。

问题 15:主从复制流程是什么?

主库事务提交
  -> 写 Binlog
  -> Dump Thread 发送 Binlog
  -> 从库 IO Thread 接收
  -> 写 Relay Log
  -> SQL Thread / Worker 回放
  -> 从库数据更新

延迟常见原因:

  1. 主库大事务;
  2. 从库回放瓶颈;
  3. 从库硬件或磁盘差;
  4. 从库承担重查询;
  5. 表缺少主键或合适索引;
  6. 网络带宽不足;
  7. DDL 回放慢。

处理:

  1. 拆小事务;
  2. 开并行复制;
  3. 下线重查询;
  4. 补索引;
  5. 临时读主要谨慎并限流;
  6. 无法收敛时重建或切换。

问题 16:异步、半同步和 MGR 怎么选?

方案 一致性 延迟 复杂度
异步复制 可能丢事务
半同步 至少一个从库确认
MGR 多数派一致 中高

加分点:不要只说方案名称,要继续分析 RPO、RTO、客户端重试、切换工具、脑裂保护和数据校验。

34.6 性能与生产治理

问题 17:慢 SQL 怎么优化?

标准流程:

1. 确认 SQL、参数、耗时、扫描行数
2. 查看执行计划
3. 判断访问类型、索引、回表、排序、临时表
4. 检查表结构和统计信息
5. 改写 SQL 或调整索引
6. 用同量级数据验证
7. 观察线上耗时和资源
8. 固化慢查询治理流程

不要只回答“加索引”。索引解决扫描问题,但不能解决大结果集传输、锁等待、网络往返、N+1 查询和不合理数据模型。

问题 18:Buffer Pool 命中率低怎么办?

先看:

SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';

再判断:

  1. 工作集是否大于内存;
  2. 是否有大扫描污染缓存;
  3. 是否刚重启导致冷缓存;
  4. SQL 是否缺索引导致随机读;
  5. 实例规格是否不足;
  6. 其他进程是否占用内存。

处理:

  1. 优化扫描 SQL;
  2. 报表切到从库或分析库;
  3. 扩容 Buffer Pool;
  4. 拆分冷热数据;
  5. 滚动重启后预热;
  6. 继续评估内存与连接模型。

问题 19:深分页怎么优化?

慢查询:

SELECT id, order_no
FROM orders
ORDER BY id
LIMIT 1000000, 20;

游标分页:

SELECT id, order_no
FROM orders
WHERE id > 1000000
ORDER BY id
LIMIT 20;

延迟关联:

SELECT o.id, o.order_no
FROM orders o
JOIN (
  SELECT id
  FROM orders
  WHERE user_id = 10001
  ORDER BY created_at DESC, id DESC
  LIMIT 1000000, 20
) AS t ON t.id = o.id;

连续列表更适合游标,后台跳页可以限制范围或使用缓存。

问题 20:高并发库存如何防超卖?

先用缓存或内存做预扣限流,MySQL 保存事实库存,并用条件更新兜底:

UPDATE sku_stock
SET available = available - 1
WHERE sku_id = 1001
  AND available >= 1;

应用检查影响行数:

int affected = statement.executeUpdate();
if (affected == 0) {
    throw new SoldOutException();
}

不要先 SELECT 再无条件 UPDATE,并发下会覆盖彼此结果。热点商品还可以拆行、合并写或异步化,但必须保留对账补偿。

问题 21:大表加字段要注意什么?

回答层次:

  1. 先确认 MySQL 版本和 DDL 类型;
  2. 判断能否 INSTANT
  3. INPLACE 要评估磁盘、IO、耗时和锁;
  4. 大表考虑 gh-ost 或 pt-osc;
  5. 执行前处理长事务,设置 lock_wait_timeout
  6. 监控 Threads_running、主从延迟和磁盘;
  7. 准备停止条件和回滚方案;
  8. 变更后验证表结构和业务 SQL。

易错点:Online DDL 不等于无锁、无 IO、无复制延迟。

34.7 场景题

场景 1:核心接口 P99 从 50ms 变成 2s

排查:

1. 看应用错误率和调用链
2. 确认是否只有数据库依赖慢
3. 查看 Threads_running、锁等待、慢日志
4. 找到 Top SQL 和执行计划变化
5. 检查近期发布、DDL、索引删除、参数修改
6. 检查主从延迟和读从库策略
7. 检查磁盘、CPU、网络和云资源限流

止血:

  1. 限流;
  2. 终止异常 SQL;
  3. 回滚发布;
  4. 切换实例;
  5. 暂停批任务;
  6. 恢复索引。

场景 2:主从延迟 10 分钟

先定位:

SHOW REPLICA STATUS\G
SELECT * FROM performance_schema.replication_applier_status_by_worker;

判断是否为单个大事务、Worker 不均、从库 SQL 慢、IO 高、Binlog 拉取阻塞或磁盘性能下降。处理上可以停主库大批任务、拆分事务、增加并行 Worker、下线从库重查询、补索引,无法收敛时重建或切换。

场景 3:误执行 UPDATE 少了 WHERE

正确流程:

1. 停止相关写入,保留现场
2. 记录误操作时间和 SQL
3. 不在原表直接乱改
4. 恢复最近备份到临时实例
5. 重放 Binlog 到误操作前
6. 比对受影响数据
7. 生成修复 SQL 和回滚 SQL
8. 双人确认后执行
9. 验证并复盘

面试重点不是背命令,而是体现“先止血、可回滚、可验证”的意识。

34.8 高频易错点

  1. REPEATABLE READ 能避免所有幻读:不准确,要区分快照读和当前读;
  2. InnoDB 只有行锁:不准确,还有间隙锁、意向锁、MDL 和 DDL 场景;
  3. 加索引一定变快:不一定,要看选择性、写放大和回表;
  4. EXPLAIN 是最终事实:它是估算,必要时用 EXPLAIN ANALYZE 和真实耗时验证;
  5. 半同步绝不丢数据:要区分确认策略、超时退化和故障场景;
  6. 分库分表解决一切:会引入跨片查询、分布式事务和扩容复杂度;
  7. 连接数越大越好:会增加内存、调度和锁竞争压力;
  8. Binlog 只用于主从:还用于备份恢复、CDC 和审计分析;
  9. 读写分离没有成本:会带来复制延迟、会话一致性和事务路由问题;
  10. 数据库问题只在数据库:也可能是调用次数、网络、事务边界和数据模型问题。

34.9 面试准备清单

必会命令:

SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS\G
SHOW REPLICA STATUS\G
SELECT * FROM sys.innodb_lock_waits;
SELECT * FROM performance_schema.data_locks;
EXPLAIN SELECT ...;
EXPLAIN ANALYZE SELECT ...;

必画图:

B+Tree 结构
MVCC 版本链与 ReadView
Redo/Binlog 两阶段提交
主从复制链路
一条 SQL 执行流程
半同步提交链路

必讲项目:

  1. 业务背景;
  2. 问题指标;
  3. 分析证据;
  4. 方案取舍;
  5. 实施步骤;
  6. 线上验证;
  7. 长期治理。

本章小结

MySQL 面试的核心是把概念讲准、把原理讲深、把场景讲活。索引题要落到执行计划、回表和扫描成本;事务题要区分隔离级别、快照读和当前读;日志题要能画两阶段提交;复制题要能分析延迟和切换风险;性能题不能只回答加索引,要给出排查闭环。最有说服力的不是标准答案,而是你能讲出一次真实问题的证据链和取舍。

思考题

  1. 为什么“MySQL 默认 RR 能避免所有幻读”这句话不准确?
  2. 为“订单列表接口变慢”设计一套完整排查脚本。
  3. 解释半同步复制在 AFTER_SYNC 下的提交顺序。
  4. 深分页有哪些方案?各自适合什么产品形态?
  5. 把你最近一次 MySQL 故障整理成面试项目,包含指标、证据、方案和验证。