MySQL 高频面试题(含口述版回答)
1. MySQL 常见存储引擎有哪些?InnoDB 和 MyISAM 的区别?
口述版:
MySQL 最常用的是 InnoDB 和 MyISAM。
InnoDB 是目前默认引擎,支持事务、行级锁、外键,有崩溃恢复能力,适合绝大多数在线业务。
MyISAM 不支持事务,只有表级锁,查询相对简单场景下可能更快,但不适合高并发写入和需要事务的业务。
核心区别可以这样记:
| 对比点 | InnoDB | MyISAM |
|---|---|---|
| 事务 | 支持 | 不支持 |
| 锁粒度 | 行级锁 | 表级锁 |
| 外键 | 支持 | 不支持 |
| 崩溃恢复 | 支持 | 不支持 |
| 计数 | 需要扫描 | 有保存值 |
| 适用场景 | 绝大多数业务 | 只读或低并发 |
现在基本都用 InnoDB,MyISAM 已经很少用了。
2. 什么是索引?有哪些类型?
口述版:
索引是帮助 MySQL 高效获取数据的数据结构,可以简单理解成书的目录。
常见类型:
- 主键索引:主键自动创建的聚簇索引,叶子节点存完整行数据。
- 唯一索引:保证列值唯一,允许 NULL。
- 普通索引:最基本的索引,没有任何约束。
- 联合索引:多个列组成的索引。
- 全文索引:用于文本搜索(InnoDB 也支持)。
从数据结构看,InnoDB 主要用 B+Tree 索引,还有哈希索引(自适应)、全文索引等。
面试重点说清:聚簇索引 vs 非聚簇索引。InnoDB 的主键是聚簇索引,叶子节点存整行;二级索引叶子节点存的是主键值,需要回表。
3. 为什么 InnoDB 选择 B+Tree 作为索引结构?
口述版:
主要原因有三点:
- B+Tree 非叶子节点不存数据,只存键值,同样大小的磁盘页能放下更多索引,树更矮,IO 次数更少。
- 叶子节点用双向链表连接,范围查询、排序非常友好,直接顺着链表走就行。
- 查询更稳定,所有数据都在叶子节点,查询路径长度一致。
对比 B Tree:B Tree 每个节点都存数据,树更高,范围查询也不方便。
哈希索引虽然单点查询快,但不支持范围查询和排序,所以 InnoDB 主要用 B+Tree。
4. 联合索引的最左前缀原则是什么?
口述版:
联合索引(比如对列 a,b,c 建索引)遵循最左前缀原则:查询条件必须从索引的最左边列开始,并且不能跳过中间列,才能有效使用索引。
举例:索引 (a, b, c)
- 能用到索引:
a/a,b/a,b,c - 不能很好用到:只有
b、只有c、b,c(没有 a)
优化时要注意:
- 把区分度高的列放在前面
- 常见查询条件尽量满足最左前缀
- 可以用
EXPLAIN看实际用了哪个索引
5. 什么是回表?什么是覆盖索引?
口述版:
回表:二级索引叶子节点只存主键值,找到主键后还要再回主键索引(聚簇索引)去取完整行数据,这个过程叫回表。多一次 IO,性能有损耗。
覆盖索引:查询的列刚好都在某个索引里,不需要回表就能拿到结果。比如索引 (a,b),查询 SELECT a,b FROM t WHERE a=1,直接从索引就能返回。
覆盖索引能减少回表,是常见的优化手段。用 EXPLAIN 看,如果 Extra 出现 Using index,通常就是用了覆盖索引。
6. 事务的四大特性(ACID)是什么?
口述版:
- A(原子性):事务中的操作要么全成功,要么全失败回滚。靠 undo log 实现。
- C(一致性):事务前后数据保持一致的业务规则(比如转账前后总金额不变)。是最终目标,由其他三个特性共同保证。
- I(隔离性):多个事务并发时互不干扰。靠锁和 MVCC 实现。
- D(持久性):事务提交后,数据永久保存,即使宕机也不丢。靠 redo log 实现。
7. MySQL 有哪些事务隔离级别?分别解决什么问题?
口述版:
从低到高四个级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
|---|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 | 最低,基本不用 |
| 读已提交(RC) | 不会 | 可能 | 可能 | Oracle 默认 |
| 可重复读(RR) | 不会 | 不会 | 可能 | MySQL 默认 |
| 串行化 | 不会 | 不会 | 不会 | 最高,性能最差 |
MySQL 默认是 可重复读(RR)。InnoDB 在 RR 下通过 MVCC + Next-Key Lock 很大程度上避免了幻读,所以实际使用体验很好。
8. 什么是 MVCC?怎么实现的?
口述版:
MVCC 是多版本并发控制,用来实现高并发下的读写不阻塞。
核心思想是:每行数据有多个版本,读的时候读快照,写的时候生成新版本,互不影响。
InnoDB 实现主要靠:
- 隐藏字段:每行有事务ID(DB_TRX_ID)和回滚指针(DB_ROLL_PTR)。
- undo log:保存旧版本数据,形成一个版本链。
- Read View:事务启动时生成一个视图,决定当前事务能看到哪些版本的数据。
在可重复读隔离级别下,一个事务全程用同一个 Read View,所以两次读到的结果一致。
9. redo log、undo log、binlog 的区别?
口述版:
这三个日志非常重要,面试常考:
| 日志 | 作用 | 层面 | 特点 |
|---|---|---|---|
| redo log | 保证持久性,崩溃恢复 | InnoDB | 物理日志,循环写 |
| undo log | 回滚 + MVCC 多版本 | InnoDB | 逻辑日志,存旧数据 |
| binlog | 主从复制、数据恢复 | Server层 | 逻辑日志,追加写 |
执行更新时的大致流程(两阶段提交):
- 先写 undo log(方便回滚)
- 更新内存数据
- 写 redo log(prepare 状态)
- 写 binlog
- 提交 redo log(commit)
两阶段提交是为了保证 redo log 和 binlog 的一致性。
10. MySQL 有哪些锁?行锁、表锁、间隙锁是什么?
口述版:
从粒度分:
- 表锁:锁整张表,开销小,但并发差。MyISAM 主要用表锁。
- 行锁:锁一行,并发高。InnoDB 支持。
InnoDB 行锁的具体实现:
- Record Lock:锁住某一行。
- Gap Lock(间隙锁):锁住一个范围的间隙,防止其他事务插入,解决幻读。
- Next-Key Lock:Record Lock + Gap Lock,锁住行本身 + 前面的间隙。这是 RR 隔离级别下的默认算法。
另外还有意向锁(表级,帮助快速判断是否有行锁冲突)。
死锁通常发生在多个事务互相持有对方需要的行锁,可以用 SHOW ENGINE INNODB STATUS 查看死锁信息。
11. 怎么排查和优化慢查询?
口述版:
步骤一般是:
- 开启慢查询日志,设置合理的
long_query_time。 - 用
mysqldumpslow或自己分析日志,找出最慢、最频繁的 SQL。 - 对可疑 SQL 执行 EXPLAIN,重点看:
type:最好是 ref、range,避免 ALL(全表扫描)key:有没有用到索引rows:扫描行数Extra:有没有 Using filesort、Using temporary
- 优化手段:
- 加合适索引(注意最左前缀)
- 避免
SELECT *,只查需要的列(方便覆盖索引) - 优化 WHERE 条件,避免对索引列使用函数
- 大数据量分页用延迟关联或记录上次位置
- 实在不行考虑分表、缓存、架构调整
12. EXPLAIN 中重要字段怎么看?
口述版:
重点关注这几个:
- type:连接类型,从好到差大致是:system > const > eq_ref > ref > range > index > ALL。日常至少要到 range 或 ref。
- key:实际用到的索引。
- rows:估算要扫描的行数,越小越好。
- Extra:
Using index:覆盖索引,好Using where:有回表或额外过滤Using filesort:需要额外排序,尽量优化掉Using temporary:用了临时表,通常性能较差
能看懂这几个,基本就能判断一条 SQL 好不好。
13. 主从复制的原理是什么?
口述版:
主从复制主要靠 binlog:
- 主库把更新操作写入 binlog。
- 从库的 IO 线程 拉取主库的 binlog,写成自己的 relay log。
- 从库的 SQL 线程 读取 relay log,重放执行,保持数据一致。
常见模式:
- 异步复制(默认):主库写完 binlog 就返回,可能丢数据。
- 半同步复制:至少有一个从库收到日志才返回。
- 组复制 / MGR:更强的一致性方案。
主从延迟是常见问题,原因可能是从库硬件差、SQL 线程单线程回放(后期有并行复制)、大事务等。
14. 什么是死锁?怎么处理?
口述版:
死锁是两个或多个事务互相持有对方需要的锁,形成环形等待。
InnoDB 有死锁检测机制,发现死锁后会回滚代价较小的那个事务,让另一个继续。
减少死锁的方法:
- 按相同顺序访问资源(比如都按主键顺序更新)。
- 尽量短事务,减少锁持有时间。
- 为常查询和更新的条件加合适索引,减少锁范围。
- 业务层做好重试机制。
排查用 SHOW ENGINE INNODB STATUS 可以看到最近的死锁日志。
15. count(*)、count(1)、count(字段) 有什么区别?
口述版:
在 InnoDB 下:
count(*)和count(1)几乎一样,都会统计所有行,优化器一般会选最小的索引来扫描。count(字段)会忽略该字段为 NULL 的行,语义不同。
MyISAM 对 count(*) 有优化(直接存了值),InnoDB 没有,所以大数据量下 count(*) 还是比较慢,需要的话可以考虑缓存计数或汇总表。
面试加分点
- 能结合项目讲:你们怎么排查慢 SQL、有没有遇到死锁、主从延迟怎么处理。
- 清楚 MVCC、redo/undo/binlog 的作用和关系。
- 会看 EXPLAIN,能说出优化思路。
- 知道覆盖索引、最左前缀、间隙锁这些实战概念。
- 对分库分表、读写分离有基本了解(可和之前分表经验结合)。
快速记忆框架:
MySQL 面试核心围绕:
存储引擎 → 索引(B+Tree、最左前缀、覆盖索引)→ 事务与隔离级别 → MVCC 与日志 → 锁机制 → 慢查询优化 → 主从复制
把每块原理和常见问题说清楚,再结合自己项目经验,基本就能应对大多数面试。