这篇适合用于互联网大厂程序员面试前突击复习。重点不是“知道名词”,而是能把每个考点讲成一段完整答案。


目录


一页速记总表

分类 高频考点 面试常问 记忆点
存储引擎 InnoDB、MyISAM 区别 为什么线上基本都用 InnoDB? InnoDB = 事务 + 行锁 + 外键 + 崩溃恢复
索引 聚簇索引、二级索引、联合索引、覆盖索引 联合索引为什么要遵守最左前缀? 最左匹配,中间不断,范围后降效
优化 Explain、回表、索引下推、慢 SQL SQL 为什么没走索引? 先看过滤,再看排序,再看回表
事务 ACID、隔离级别、幻读 RR 为什么还能防幻读? Undo 保旧,ReadView 判可见,Next-Key 防插入
并发 行锁、间隙锁、临键锁、死锁 死锁是怎么产生的? 资源争抢 + 加锁顺序不一致
日志 redo log、undo log、binlog redo 和 binlog 有什么区别? redo 保数据,binlog 保归档
主从 主从复制、读写分离、主从延迟 为什么会主从不一致? 主写 binlog,从拉 relay log,再回放
设计 三大范式、反范式、分库分表 什么时候要分库分表? 单表过大、并发过高、单机到顶

MySQL 架构与存储引擎

1. MySQL 基本架构

可以先把 MySQL 理解成两层:

  • Server 层:连接器、分析器、优化器、执行器
  • 存储引擎层:负责真正读写数据,最常见的是 InnoDB

记忆点

  • Server 层做解析和调度,存储引擎层做存取和落盘
  • 面试回答时,先讲“分层”,再讲“引擎”

2. InnoDB 和 MyISAM 的区别

这是老牌高频题,建议直接背表格式表达。

对比项 InnoDB MyISAM
事务 支持 不支持
行锁 表锁
外键 支持 不支持
崩溃恢复 支持
索引组织 聚簇索引 非聚簇
适用场景 OLTP、高并发写入 读多写少、历史场景

高频结论

  • 线上业务库几乎默认选择 InnoDB
  • 因为它支持事务、行级锁、MVCC、崩溃恢复,更适合互联网高并发场景

记忆点

  • 线上选 InnoDB,不是因为快,而是因为稳

索引

索引是 MySQL 面试的绝对核心,很多大厂一面二面都会深挖。

1. 什么是索引

索引的本质是帮助数据库快速定位数据的数据结构。MySQL 的 InnoDB 默认使用 B+Tree

为什么不用普通二叉树或红黑树?

  • 二叉树层高太高,磁盘 IO 多
  • 红黑树虽然平衡,但分叉少,仍然会导致树高较高
  • B+Tree 一个节点能存很多键值,树更矮,范围查询也更友好

记忆点

  • 索引不是为了排序发明的,是为了减少查找成本
  • B+Tree 适合磁盘,Hash 适合等值

2. 聚簇索引和二级索引

InnoDB 中,主键索引就是聚簇索引,数据行本身就存放在主键索引叶子节点上。

  • 聚簇索引叶子节点:存整行数据
  • 二级索引叶子节点:存索引列值 + 主键值

所以通过二级索引查整行数据时,往往要先找到主键,再回到聚簇索引中取完整数据,这个过程叫 回表

记忆点

  • 主键树挂整行,二级树挂主键
  • 查二级索引后再查主键树 = 回表

3. 覆盖索引

如果查询的列,刚好都在索引里,就不需要回表,这就是覆盖索引。

例如:

1
2
3
SELECT id, name
FROM user
WHERE age = 20;

如果存在联合索引 (age, name),再加上主键 id 也能从索引中拿到,那么可能直接覆盖,不再回表。

记忆点

  • 能在索引里拿全,就别回表
  • 覆盖索引常用于优化查询列表字段过多的问题

4. 联合索引与最左前缀原则

联合索引例如 (a, b, c),能使用的前提是从左往右连续匹配。

可用情况:

  • a
  • a, b
  • a, b, c

可能部分可用或失效的情况:

  • 只查 bc
  • a 用范围查询后,再利用 bc 的能力会下降

经典口诀

  • 最左匹配,中间不断,范围后降效

这句话几乎可以直接背。

5. 索引失效常见场景

高频失效点建议直接记:

  • 对索引列做函数计算,例如 DATE(create_time)
  • 对索引列做表达式运算,例如 age + 1 = 30
  • 隐式类型转换,例如字符串列和数字比较
  • %abc 开头的模糊匹配
  • 联合索引不满足最左前缀
  • 使用 OR 且其中一部分条件无法走索引

记忆点

  • 索引列要保持“干净”
  • 所谓干净,就是不要在索引列上做函数、运算、类型转换

6. 什么情况下要建索引

适合建索引的字段:

  • 高频作为查询条件的字段
  • 高频用于排序、分组的字段
  • 多表关联中的关联字段
  • 区分度较高的字段

不适合建索引的字段:

  • 区分度很低,比如性别
  • 更新非常频繁的字段
  • 很长的大字段

记忆点

  • 查询多、筛选强、排序多、关联多,就考虑建索引
  • 写多、重复高、字段大,不要乱建索引

SQL 执行过程与优化

1. 一条 SQL 的执行过程

可以按下面顺序回答:

  1. 客户端先连接到 MySQL
  2. Server 层进行词法分析、语法分析
  3. 优化器选择执行计划
  4. 执行器调用存储引擎
  5. 存储引擎返回数据

记忆点

  • 连接器 -> 分析器 -> 优化器 -> 执行器 -> 存储引擎

2. Explain 重点看什么

常见字段:

  • type:访问类型,性能从好到差大致是 system > const > eq_ref > ref > range > index > ALL
  • key:实际使用的索引
  • rows:预估扫描行数
  • Extra:额外信息,比如 Using indexUsing filesortUsing temporary

重点理解

  • Using index 通常说明走了覆盖索引
  • Using filesort 说明排序无法直接利用索引
  • Using temporary 常见于分组、排序不合理

记忆点

  • 先看 type,再看 key,再看 rows,最后看 Extra

3. 慢 SQL 优化思路

一套标准回答模板:

  1. 先用慢查询日志或监控找到 SQL
  2. EXPLAIN 分析执行计划
  3. 看是否命中合适索引
  4. 看是否存在回表、排序、临时表
  5. 看 SQL 是否能改写
  6. 不行再考虑表结构调整、分库分表、缓存

记忆点

  • 优化 SQL,不是上来就加索引,而是先看执行计划

4. 常见优化手段

  • 避免 SELECT *
  • 分页使用延迟关联或基于主键翻页
  • where、order by、group by 尽量共用索引
  • 小表驱动大表
  • 批量插入代替单条插入
  • 合理控制事务大小

高频追问

为什么不建议深分页?

  • 因为 LIMIT offset, size 的 offset 很大时,MySQL 仍然要先扫描并丢弃前面的数据,成本很高

记忆点

  • 深分页慢,不是返回慢,是跳过慢

事务与隔离级别

1. ACID

事务四大特性:

  • Atomicity 原子性:要么都成功,要么都失败
  • Consistency 一致性:事务前后数据满足约束
  • Isolation 隔离性:并发事务之间互不干扰
  • Durability 持久性:提交后数据不丢

记忆点

  • A 原子,C 一致,I 隔离,D 持久

2. 四种隔离级别

隔离级别 脏读 不可重复读 幻读
读未提交 RU 可能 可能 可能
读已提交 RC 不会 可能 可能
可重复读 RR 不会 不会 理论上可能,InnoDB 中被大幅抑制
串行化 Serializable 不会 不会 不会

MySQL InnoDB 默认隔离级别是 RR

3. 脏读、不可重复读、幻读

  • 脏读:读到了别人还没提交的数据
  • 不可重复读:同一事务内,两次读同一行结果不一致
  • 幻读:同一事务内,两次按条件查询,记录条数不一致

记忆点

  • 脏读看的是“未提交内容”
  • 不可重复读看的是“同一行变了”
  • 幻读看的是“符合条件的行数变了”

MVCC

MVCC 是 MySQL 面试里的难点,但也是非常拉开差距的地方。

1. MVCC 是什么

MVCC 即多版本并发控制,让读操作尽量不加锁,通过读取数据的历史版本来提升并发能力。

它主要依赖:

  • undo log
  • 隐藏字段
  • Read View

2. Read View 做了什么

可以简单理解为:事务在读数据时,MySQL 会拿到一个“可见性快照”,判断某条记录版本对当前事务是否可见。

你不必在面试里把源码级细节背死,但至少要会说:

  • 每次更新不会直接覆盖老版本,而是通过 undo log 保留旧版本链
  • 一致性读会根据 Read View 判断该读哪个版本

记忆点

  • undo log 存老版本,Read View 决定看哪个版本

3. RC 和 RR 在 MVCC 上的差异

  • RC:每次普通 select 都可能生成新的 Read View
  • RR:事务第一次一致性读生成 Read View,后续复用,所以可重复读

一句话记忆

  • RC 是句级快照,RR 是事务级快照

锁机制

1. 行锁、表锁、间隙锁、临键锁

  • 行锁:锁住某一行记录
  • 表锁:锁住整张表
  • 间隙锁:锁住索引记录之间的区间,防止插入
  • 临键锁:记录锁 + 间隙锁

InnoDB 的行锁是基于索引实现的,如果查询没走索引,可能退化成较大范围的锁,甚至看起来像表锁效果。

记忆点

  • 行锁锁的不是行本身,而是索引上的记录

2. 为什么会有幻读,MySQL 怎么解决

在当前读场景下,单靠 MVCC 不够,还要借助间隙锁/临键锁来阻止其他事务插入满足条件的新记录。

所以常见面试回答可以这样说:

  • 快照读主要靠 MVCC
  • 当前读主要靠 Next-Key Lock

记忆点

  • 快照读靠版本,当前读靠加锁

3. 死锁

死锁常见成因:

  • 两个事务互相持有对方需要的锁
  • 多个事务获取锁的顺序不一致

避免思路:

  • 固定加锁顺序
  • 尽量让事务短小
  • 尽量走索引,缩小锁范围

记忆点

  • 死锁的本质不是锁多,而是顺序乱

日志体系

日志体系是大厂非常爱问的一组题,往往和事务一起问。

1. redo log

redo log 是 InnoDB 特有的重做日志,用来保证事务提交后的持久性。

为什么需要它?

  • 因为数据页在内存修改后,不会立刻刷盘
  • 如果宕机,可能磁盘上的数据页还没来得及写回
  • 这时可以靠 redo log 重放恢复

记忆点

  • redo 是“做过什么”,用于宕机恢复

2. undo log

undo log 记录数据修改前的旧版本,主要作用:

  • 事务回滚
  • 支持 MVCC

记忆点

  • undo 是“改之前什么样”

3. binlog

binlog 是 MySQL Server 层的归档日志,常用于:

  • 主从复制
  • 数据恢复
  • 审计

和 redo log 的区别:

  • redo log 是 InnoDB 引擎层日志
  • binlog 是 Server 层日志
  • redo log 偏物理
  • binlog 偏逻辑

记忆点

  • redo 保持库不坏,binlog 负责传出去

4. 两阶段提交

当事务提交时,为了保证 redo logbinlog 一致,MySQL 会采用两阶段提交。

简化理解:

  1. 写 redo log,状态为 prepare
  2. 写 binlog
  3. 提交 redo log

为什么要这样做?

  • 避免只写了其中一个日志,导致主从复制和数据恢复出现不一致

记忆点

  • 先 prepare,再写 binlog,最后 commit redo

主从复制与高可用

1. 主从复制基本流程

高频回答模板:

  1. 主库写入数据
  2. 主库记录 binlog
  3. 从库 IO 线程拉取主库 binlog
  4. 写入从库 relay log
  5. 从库 SQL 线程回放 relay log

记忆点

  • 主写 binlog,从拉 relay log,再重放

2. 主从延迟原因

常见原因:

  • 从库执行能力弱于主库写入速度
  • 大事务回放慢
  • 从库还有查询压力
  • 网络延迟

常见应对:

  • 避免大事务
  • 提升从库配置
  • 关键读请求走主库
  • 监控复制延迟

记忆点

  • 主从延迟,本质是从库追不上主库

3. 读写分离的风险

最大风险是:

  • 主库刚写,从库还没同步完成,用户立刻去从库读取时读到旧数据

这就是常说的“延迟读”或“读写不一致”。

常见解决方案:

  • 强一致读走主库
  • 写后短时间内读主库
  • 利用中间件做路由控制

数据库设计高频考点

1. 三大范式

  • 第一范式:列不可再分
  • 第二范式:非主属性完全依赖主键
  • 第三范式:非主属性不传递依赖主键

但真实业务里经常会适度反范式,以提升查询性能。

记忆点

  • 范式保规范,反范式保性能

2. 为什么不建议使用过长字符串做主键

因为主键是聚簇索引,主键越大:

  • 主键索引更大
  • 二级索引也会更大
  • 页能装下的数据更少
  • IO 成本更高

所以通常推荐:

  • 自增主键
  • 雪花 ID

记忆点

  • 主键一大,整棵索引树都跟着胖

3. 什么时候需要分库分表

典型信号:

  • 单表数据量过大
  • 单机存储到瓶颈
  • 并发量过高
  • 单库成为系统瓶颈

分库分表带来的问题:

  • 分布式事务
  • 跨库 join
  • 全局 ID
  • 分页与排序复杂

记忆点

  • 分库分表解决容量和并发,也引入复杂度

面试高频追问清单

下面这些题,出现频率非常高,建议逐条能讲 1 到 3 分钟。

索引类

  • 为什么 MySQL 索引选择 B+Tree?
  • 聚簇索引和非聚簇索引的区别是什么?
  • 什么是回表?什么是覆盖索引?
  • 联合索引为什么要遵守最左前缀?
  • 索引下推了解吗?
  • 什么情况下索引会失效?

事务类

  • ACID 分别是什么?
  • MySQL 默认隔离级别是什么?
  • 什么是脏读、不可重复读、幻读?
  • RC 和 RR 的区别是什么?
  • MVCC 是怎么实现的?

锁类

  • 行锁为什么依赖索引?
  • 什么是间隙锁?什么是临键锁?
  • InnoDB 如何避免幻读?
  • 死锁是怎么发生的?怎么排查?

日志类

  • redo log 和 binlog 的区别?
  • undo log 的作用是什么?
  • 两阶段提交是为了解决什么问题?
  • 为什么提交事务后不一定立刻刷数据页?

架构类

  • 主从复制原理是什么?
  • 主从延迟怎么解决?
  • 分库分表什么时候做?
  • 读写分离有什么坑?

背诵口诀

如果你想在面试前一天快速过一遍,可以直接背下面这些短句。

1. 索引口诀

最左匹配,中间不断,范围后降效。

主键树挂整行,二级树挂主键。

能覆盖不回表,索引列要干净。

2. 事务口诀

RC 句级快照,RR 事务快照。

undo 存旧版本,Read View 判可见。

快照读靠 MVCC,当前读靠 Next-Key Lock。

3. 日志口诀

redo 保持久,undo 保回滚,binlog 保归档。

先 prepare,再写 binlog,最后提交 redo。

4. 调优口诀

先看慢日志,再看 Explain。

先看 type,再看 key,再看 rows,最后看 Extra。

优化不是乱加索引,而是减少扫描、减少回表、减少排序。

5. 架构口诀

主写 binlog,从拉 relay log,再重放。

分库分表抗规模,也会带来分布式复杂度。


最后怎么答得像大厂候选人

面试官问 MySQL,最好不要只给“定义式答案”,而要按下面这个顺序讲:

  1. 先给一句结论
  2. 再讲原理
  3. 再补一个使用场景或线上经验
  4. 最后补一句优缺点或注意事项

例如回答“为什么联合索引要遵守最左前缀”,可以按这个模板:

  • 结论:联合索引底层按索引列从左到右有序组织,所以必须尽量从最左列开始匹配
  • 原理:B+Tree 节点比较时先比较第一列,再比较第二列、第三列
  • 场景:如果直接跳过最左列,后面的有序性通常无法被单独利用
  • 注意点:遇到范围查询后,后续列继续利用索引排序和过滤的能力会下降

这类回答会明显比“只背定义”更像真正理解过。