📖 本篇简介:原理简答题是面试的"地基题"——答得出来不一定加分,答不上直接出局。本篇精选 20 道最高频原理题,覆盖 B+ 树、聚簇索引、MVCC、ACID、三种日志、隔离级别、锁、Buffer Pool、最左前缀、两阶段提交等。每题给要点化答案和追问应对。前置知识:第 1、3、6、7 章;本系列示例基于 MySQL 8.0。
目录
- 1 本篇考情与答题原则
- 题目 1:为什么索引用B+树
- 题目 2:聚簇索引和非聚簇索引区别
- 题目 3:MVCC怎么实现
- 题目 4:事务ACID分别靠什么保证
- 题目 5:redo undo binlog区别
- 题目 6:事务隔离级别解决什么问题
- 题目 7:间隙锁解决什么问题
- 题目 8:索引为什么能加速查询
- 题目 9:什么是覆盖索引
- 题目 10:什么是回表
- 题目 11:MySQL有哪些锁
- 题目 12:主从复制原理
- 题目 13:为什么推荐自增主键
- 题目 14:脏读不可重复读幻读区别
- 题目 15:Buffer Pool的作用
- 题目 16:最左前缀原则
- 题目 17:redo log的作用
- 题目 18:为什么范围查询不用哈希索引
- 题目 19:两阶段提交是什么
- 题目 20:为什么慢查询要建合适索引
- 速查速记
- 常见坑与注意事项
- 复习清单
- 练习题
1 本篇考情与答题原则
原理简答考的是"能不能用自己的话讲清机制"。答题原则:先说结论,再讲机制,最后举个例子。别只背名词。
| 答题层次 | 示例 |
|---|---|
| 结论 | "MVCC 让读写不阻塞" |
| 机制 | "靠 undo 版本链 + ReadView 判断可见性" |
| 例子 | "RR 下事务第一次 SELECT 生成 ReadView,之后复用" |
题目 1:为什么索引用B+树
考点
- 矮胖;叶子链表;磁盘 IO。
参考答案
1. B+ 树矮胖:非叶子不存数据,扇出大,3~4 层存千万行,树高低 = IO 少;
2. 叶子双向链表:范围查询顺链表扫,快;
3. 所有查询到叶子:路径稳定;
4. 按页读写:一页 16KB,一次带很多索引项;
5. 对比:B 树非叶子存数据树高,哈希不支持范围。
-- 索引结构对比,本示例基于 MySQL 8.0
B+树 : 非叶子不存数据, 扇出大, 3~4层存千万行, 叶子链表适合范围
B树 : 非叶子也存数据, 扇出小, 树更高
哈希 : 只支持等值, 不支持范围/排序
追问
- 问:树高大概多少?答:千万行 3~4 层,3~4 次磁盘 IO。
- 问:哈希索引不好吗?答:只等值,不范围。
题目 2:聚簇索引和非聚簇索引区别
考点
- 叶子节点内容;回表。
参考答案
1. 聚簇索引(主键):叶子存整行数据,一表一个;
2. 二级(非聚簇)索引:叶子存主键值;
3. 回表:二级索引查到主键再回聚簇索引取行;
4. 覆盖索引:查询列在二级索引里免回表;
5. 没主键:InnoDB 选唯一非空索引,否则生成隐藏。
追问
- 问:回表代价?答:多一次 B+ 树查找 + 随机 IO。
- 问:InnoDB 和 MyISAM 索引区别?答:MyISAM 都是非聚簇,叶子存地址。
题目 3:MVCC怎么实现
考点
- undo 链;ReadView。
参考答案
1. 隐藏列:trx_id、roll_pointer;
2. undo 版本链:旧版本串成链;
3. ReadView:活跃事务列表判断可见性;
4. RC/RR:ReadView 生成时机不同;
5. 效果:快照读不加锁,读写不阻塞。
追问
- 问:怎么判断可见?答:版本 trx_id 在 ReadView 中,按规则比较。
- 问:当前读呢?答:读最新,靠锁。
题目 4:事务ACID分别靠什么保证
考点
- 各特性机制。
参考答案
1. A 原子性:undo log 回滚;
2. C 一致性:由 AID + 业务约束达成;
3. I 隔离性:锁 + MVCC;
4. D 持久性:redo log WAL;
5. 两阶段提交协调 redo/binlog。
追问
- 问:一致性是手段还是目的?答:目的,其他三性为它服务。
- 问:持久性靠什么刷盘?答:redo 先刷,crash 重放。
题目 5:redo undo binlog区别
考点
- 三种日志职责。
参考答案
1. redo:InnoDB 物理日志,crash-safe;
2. undo:InnoDB 逻辑日志,回滚 + MVCC;
3. binlog:Server 层逻辑日志,复制 + 恢复;
4. redo 循环写,binlog 追加写;
5. 两阶段提交保证二者一致。
-- 三种日志职责对比,本示例基于 MySQL 8.0
redo : InnoDB物理日志, 记录页修改, 保证crash-safe
undo : InnoDB逻辑日志, 记录反向操作, 用于回滚+MVCC
binlog: Server层逻辑日志, 记录全部变更, 用于主从复制
追问
- 问:为什么有 redo 还要 binlog?答:redo 管 crash 恢复,binlog 管复制和点恢复。
- 问:redo 记录什么?答:某页某偏移改了什么。
题目 6:事务隔离级别解决什么问题
考点
- 四种级别;并发问题。
参考答案
1. 读未提交:啥都不防;
2. 读提交 RC:防脏读;
3. 可重复读 RR:防脏读、不可重复读(InnoDB 还防幻读);
4. 串行化:全防但性能差;
5. 默认 RR:MySQL InnoDB 默认 RR。
追问
- 问:互联网常用哪个?答:RC,并发更高、锁更少。
- 问:RR 一定不幻读?答:快照读防,当前读靠临键锁。
题目 7:间隙锁解决什么问题
考点
- 幻读;锁范围。
参考答案
1. 间隙锁:锁索引记录之间的间隙,防别的事务插入;
2. 解决 RR 下的幻读:当前读时别让新行插入;
3. 临键锁:记录锁 + 间隙锁;
4. 只在 RR 出现:RC 无间隙锁;
5. 代价:锁范围大,并发下降。
追问
- 问:什么时候退化为记录锁?答:唯一索引等值命中唯一记录。
- 问:间隙锁什么时候加?答:RR 范围查询/当前读。
题目 8:索引为什么能加速查询
考点
- 减少扫描行数;有序定位。
参考答案
1. 没有索引:全表扫描逐行比对;
2. 有索引:B+ 树按 key 定位,从 O(n) 降到 O(log n);
3. 有序:范围查询、排序可利用索引顺序;
4. 覆盖索引:免回表更快;
5. 代价:占空间、拖慢写入。
追问
- 问:索引越多越好?答:不是,写放大、优化器选索引成本高。
- 问:什么列该建索引?答:高频 where/join/order by、高选择性列。
题目 9:什么是覆盖索引
考点
- 免回表;Extra 显示。
参考答案
1. 定义:查询要的列都在索引里,不必回表;
2. EXPLAIN Extra:Using index;
3. 好处:少回表随机 IO;
4. 设计:把高频查询列加进联合索引;
5. 代价:索引变宽。
追问
- 问:怎么看有没有覆盖?答:Extra 出现 Using index。
- 问:覆盖索引能含大字段?答:宽索引占空间,权衡。
题目 10:什么是回表
考点
- 二级索引 + 主键取行。
参考答案
1. 过程:二级索引查到主键 → 再到聚簇索引取整行;
2. 代价:多一次 B+ 树查找 + 随机 IO;
3. 避免:覆盖索引;
4. 触发:查询列不全在二级索引;
5. 深分页 + 回表:慢查询常见原因。
追问
- 问:怎么减少回表?答:覆盖索引、延迟关联。
- 问:主键查询回表吗?答:聚簇索引本身就是行,不回表。
题目 11:MySQL有哪些锁
考点
- 粒度;算法。
参考答案
1. 按粒度:表锁、行锁;
2. 行锁算法:记录锁、间隙锁、临键锁;
3. 按性质:共享锁 S、排他锁 X;
4. 意向锁:表级,快速判断表内行锁;
5. MDL:元数据锁,DDL/DML 互斥。
追问
- 问:什么时候加表锁?答:DDL、lock tables、全表更新。
- 问:意向锁干嘛?答:DML 前先加意向锁,DDL 快速判断表能否锁。
题目 12:主从复制原理
考点
- 三线程;binlog。
参考答案
1. 主库写 binlog;
2. 从库 IO 线程拉 binlog 写 relay log;
3. SQL 线程重放;
4. 格式:STATEMENT/ROW/MIXED,常用 ROW;
5. 8.0 并行复制降低延迟。
追问
- 问:主从延迟原因?答:单线程重放、大事务、从库性能。
- 问:半同步?答:主库等从库 ACK。
题目 13:为什么推荐自增主键
考点
- 顺序写;页分裂;体积。
参考答案
1. 顺序插入尾部:不页分裂;
2. BIGINT 短:二级索引存主键更省空间;
3. 随机 UUID:页分裂 + 碎片;
4. 聚簇索引顺序写性能高;
5. 分库后:趋势递增分布式 ID。
追问
- 问:UUID 真的不能用?答:可做业务展示,别当聚簇主键。
- 问:自增主键热点?答:8.0 已优化为轻量 mutex。
题目 14:脏读不可重复读幻读区别
考点
- 三种并发异常。
参考答案
1. 脏读:读到别的事务未提交的数据;
2. 不可重复读:同一事务两次读同一行,别人提交后变了(侧重改);
3. 幻读:同一事务两次范围查询,别人插入了新行(侧重增);
4. RC 防脏读;RR 防不可重复读;串行化全防;
5. InnoDB RR:MVCC 防幻读快照,临键锁防当前读幻读。
追问
- 问:不可重复读和幻读区别?答:前者针对同一行被改,后者针对范围多出新行。
- 问:怎么防幻读?答:临键锁锁住间隙。
题目 15:Buffer Pool的作用
考点
- 内存缓存;脏页。
参考答案
1. 作用:缓存数据页/索引页,避免每次读磁盘;
2. 写先改内存页(脏页),后台刷盘;
3. 改良 LRU:young/old 两段防全表扫污染;
4. 大小:物理内存 50%~70%;
5. 命中率:高 = 性能好。
追问
- 问:脏页什么时候刷?答:checkpoint、redo 快满、脏页比例高。
- 问:刷脏为什么卡?答:同步刷盘占 IO。
题目 16:最左前缀原则
考点
- 联合索引顺序;跳过前导列。
参考答案
1. 联合索引 (a,b,c):查询从最左列开始匹配;
2. 跳过 a 直接查 b:用不上索引;
3. 范围后失效:a=1 AND b>2 AND c=3——c 用不上;
4. 等值可重排:优化器能调换等值条件顺序;
5. 设计:等值列在前,范围列在后。
-- 联合索引(a,b,c)的命中情况,本示例基于 MySQL 8.0
WHERE a=1 AND b=2 AND c=3 -- 全用到
WHERE a=1 AND c=3 -- 只用到a(跳过b)
WHERE a=1 AND b>2 AND c=3 -- 用到a,b;c排在范围后只能ICP过滤
WHERE b=2 -- 用不到(跳过前导列a)
追问
- 问:a>1 AND b=2 哪个先?答:范围 a 在前,b 也用不上(范围后失序)。
- 问:怎么验证?答:EXPLAIN 看 key_len 和 type。
题目 17:redo log的作用
考点
- WAL;持久性;crash 恢复。
参考答案
1. 先写 redo 再写数据页(WAL):crash 后重放;
2. 保证持久性:已提交事务不丢;
3. 循环写:固定大小,checkpoint 推进;
4. 崩溃恢复:重启重放 redo;
5. 双 1:innodb_flush_log_at_trx_commit=1 最安全。
追问
- 问:redo 和 binlog 谁先?答:两阶段,redo prepare 先于 binlog。
- 问:redo 太大?答:恢复慢;太小频繁刷。
题目 18:为什么范围查询不用哈希索引
考点
- 哈希无序。
参考答案
1. 哈希索引:哈希后无序,只支持等值;
2. 范围查询:>、<、BETWEEN 无法用哈希定位;
3. 排序/前缀:哈希不支持;
4. B+ 树:叶子有序链表,天然范围;
5. 自适应哈希:InnoDB 内部自动给热点等值建哈希。
追问
- 问:Memory 引擎哈希索引?答:内存表支持,但 InnoDB 默认 B+ 树。
- 问:等值查询哈希不是快?答:但数据库要范围/排序,B+ 通用。
题目 19:两阶段提交是什么
考点
- redo 与 binlog 一致;崩溃恢复。
参考答案
1. prepare:写 redo 置 prepare;
2. commit:写 binlog,redo 置 commit;
3. 崩溃恢复:扫描 redo,prepare 看 binlog 完整与否决定提交/回滚;
4. 目的:redo 与 binlog 一致,主从不脱节;
5. 否则:主库提交了但 binlog 没写,从库少数据。
-- 两阶段提交时序,本示例基于 MySQL 8.0
阶段1 prepare: 写 redo log 并置 prepare
阶段2 commit : 写 binlog, 再把 redo 置 commit
崩溃恢复: 扫描redo, prepare态看binlog是否完整决定提交/回滚
追问
- 问:为什么必须两阶段?答:给"提交"一个跨日志的一致判定点。
- 问:prepare 后崩溃?答:binlog 完整就提交,否则回滚。
题目 20:为什么慢查询要建合适索引
考点
- 全表扫描代价;索引选择性。
参考答案
1. 无索引全表扫:逐行比对,CPU/IO 高;
2. 合适索引:定位扫描行数骤降;
3. 不是建了就有用:写法失效、选择性差会白建;
4. 覆盖索引:连回表都省;
5. EXPLAIN:建完验证 type/rows。
追问
- 问:索引建错列?答:优化器不选,等于没建。
- 问:加索引要注意?答:写放大,控制数量。
速查速记
| 原理 | 一句话 | 关键词 |
|---|---|---|
| B+树 | 矮胖+叶子链 | 3~4层 |
| MVCC | undo链+ReadView | 读写不阻塞 |
| A/D/I | undo/redo/锁+MVCC | 三性分工 |
| 三日志 | 物理/逻辑/复制 | 两阶段 |
| 隔离级别 | RC防脏RR防重复 | 默认RR |
| 间隙锁 | RR锁间隙防幻读 | RC没有 |
| 覆盖索引 | 免回表 | Using index |
| 最左前缀 | 从左列开始 | 范围后失效 |
常见坑与注意事项
- 坑 1:把不可重复读和幻读混为一谈——前者是同行被改,后者是范围多出新行;
- 坑 2:以为 RC 有间隙锁——RC 只有记录锁,并发更高;
- 坑 3:说覆盖索引就是主键索引——覆盖是"查询列都在索引里",可指任何二级索引;
- 坑 4:以为索引建了就一定生效——写法失效、选择性差,优化器会放弃;
- 坑 5:说 InnoDB count(*) 是 O(1)——那是 MyISAM,InnoDB 要数可见行;
- 注意:本题库为通用原理,按 MySQL 8.0 讲解。
复习清单
| 题目 | 核心 | 易错点 |
|---|---|---|
| B+树 | 矮胖+链表 | 别和B树混 |
| MVCC | undo链+ReadView | RC/RR时机 |
| ACID | undo/redo/锁 | C是目的 |
| 三日志 | redo物理/binlog逻辑 | 两阶段 |
| 隔离级别 | RC/RR/串行 | RR防幻读 |
| 间隙锁 | RR当前读 | RC没有 |
| 最左前缀 | 等值在前范围在后 | 跳过前导列 |
| 两阶段 | prepare/commit | 崩溃看binlog |
练习题
练 1:用一段话讲清 MVCC 怎么实现可重复读。
<details>
<summary>参考答案</summary>
每行有 trx_id 和 roll_pointer,修改时在 undo 里留旧版本串成版本链;RR 事务在第一次 SELECT 时生成 ReadView(记录活跃事务列表),之后该事务读时沿版本链找第一个满足 ReadView 可见规则的版本,因此看不到别的事务后来提交的修改,实现可重复读。
</details>
练 2:联合索引 (a,b,c),查询 WHERE a=1 AND c=3 能用到索引吗?WHERE a=1 AND b>2 AND c=3 呢?
<details>
<summary>参考答案</summary>
a=1 AND c=3:能用到 a(最左前缀),c 用不上(跳过 b)。a=1 AND b>2 AND c=3:a、b 能定位,b 是范围列,c 排在范围之后失序,只能靠 ICP 在索引层过滤 c,但不能用 c 定位。
</details>
下一篇预告:第 38 章 高频场景设计真题精讲
文章回复
0 条公开回复