12-MySQL面试题汇总

#mysql #数据库 #面试 #review

总结
  • 高频面试题覆盖:索引、事务、锁、日志、调优、主从复制
  • 每道题都附有简明答案和详细链接
  • 面试前重点复习:B+树、MVCC、二阶段提交、索引失效场景、慢查询优化

1. 索引相关

Q1:为什么 InnoDB 用 B+ 树而不是 B 树?

详细内容见 索引原理

对比 B 树 B+ 树
数据存储 所有节点都存数据 只有叶子节点存数据
叶子节点 无链表 双向链表连接
范围查询 需要中序遍历 叶子节点顺序扫描
磁盘 IO 相同数据量树更高 树更矮,IO 更少

答案

  1. B+ 树非叶子节点不存数据,一个页能存更多索引项,树更矮,减少磁盘 IO
  2. 叶子节点用双向链表连接,范围查询直接顺序扫描,不用回溯
  3. 查询性能稳定,任何查询都要走到叶子节点

Q2:聚簇索引和非聚簇索引有什么区别?

详细内容见 聚簇索引 vs 普通索引

答案

Q3:什么是回表?怎么避免?

答案:用非聚簇索引查到主键后,还要去聚簇索引查完整数据,这就是回表。

避免方法:使用覆盖索引,让查询需要的字段都在索引里。

-- 联合索引 (name, age)
SELECT name, age FROM user WHERE name = '张三';  -- 覆盖索引,不回表
SELECT * FROM user WHERE name = '张三';           -- 需要回表

Q4:联合索引的最左前缀原则是什么?

详细内容见 联合索引匹配规则

答案:联合索引 (a, b, c) 的查询规则:

Q5:哪些情况下索引会失效?

详细内容见 索引失效场景

高频场景

  1. 对索引字段用函数:WHERE YEAR(create_time) = 2024
  2. 隐式类型转换:WHERE phone = 13800138000(phone 是 varchar)
  3. LIKE 左模糊:WHERE name LIKE '%三'
  4. 联合索引跳过中间字段:WHERE a=1 AND c=3(跳过 b)
  5. OR 条件没全建索引:WHERE a=1 OR b=2(只有 a 有索引)

2. 事务相关

Q6:事务的四大特性(ACID)是什么?

特性 说明 实现机制
原子性(Atomicity) 事务要么全成功,要么全失败 undo log
一致性(Consistency) 数据从一个一致状态到另一个一致状态 其他三个特性的共同保证
隔离性(Isolation) 并发事务互不干扰 锁 + MVCC
持久性(Durability) 提交后数据永久保存 redo log

Q7:四种隔离级别分别解决什么问题?

详细内容见 事务隔离级别

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

MySQL 默认 RR 级别,且能避免幻读(靠 MVCC + 间隙锁)。

Q8:MVCC 是怎么实现的?

详细内容见 MVCC 机制

答案:两个核心组件:

  1. undo log 版本链:每行数据有隐藏字段 trx_idroll_pointer,串成版本链
  2. ReadView:事务查询时生成快照,包含 m_ids、min_trx_id、max_trx_id、creator_trx_id

RC 每次查询重新生成 ReadView,RR 事务内只生成一次。

Q9:redo log 和 binlog 有什么区别?

详细内容见 日志系统

对比 redo log binlog
所属层 InnoDB Server
日志类型 物理日志 逻辑日志
用途 崩溃恢复 主从复制、数据恢复
写入方式 循环写 追加写

Q10:什么是二阶段提交?为什么需要?

详细内容见 二阶段提交

答案:事务提交分两步:

  1. prepare:写 redo log,标记为 prepare 状态
  2. commit:写 binlog,然后把 redo log 标记为 commit

为什么需要:保证 redo log 和 binlog 一致。崩溃恢复时:

3. 锁相关

Q11:InnoDB 有哪些锁?

详细内容见 锁机制详解

按粒度分:全局锁、表锁、行锁
按模式分:共享锁(S)、排他锁(X)、意向锁(IS/IX)
按算法分:记录锁、间隙锁、临键锁

Q12:什么是间隙锁?有什么用?

答案:锁的是索引记录之间的间隙,防止其他事务在间隙里插入数据,用于解决 RR 级别的幻读。

-- 假设 id 有 10, 20, 30
SELECT * FROM user WHERE id > 15 AND id < 25 FOR UPDATE;
-- 锁住间隙 (10, 20) 和 (20, 30)

Q13:怎么避免死锁?

答案

  1. 按相同顺序访问表和行
  2. 大事务拆成小事务
  3. 确保 WHERE 条件用上索引,避免锁升级为表锁
  4. 设置合理的锁等待超时时间

4. 调优相关

Q14:慢查询怎么排查?

详细内容见 执行计划分析SQL调优案例

步骤

  1. 开启慢查询日志:slow_query_log = ON
  2. 找到慢 SQL:mysqldumpslow 或直接看日志
  3. EXPLAIN 分析执行计划
  4. 看 type(有没有 ALL)、key(有没有走索引)、Extra(有没有 filesort)
  5. 针对性优化:加索引、改写 SQL、调整表结构

Q15:Buffer Pool 怎么配置?

详细内容见 Buffer Pool 配置

答案

Q16:深分页怎么优化?

详细内容见 评论系统深分页

答案:子查询先查 ID 再回表

-- 慢:回表 10 万次
SELECT * FROM comments WHERE product_id='xxx' LIMIT 100000, 20;

-- 快:子查询只查 ID(覆盖索引),再回表 20 次
SELECT * FROM comments a,
(SELECT id FROM comments WHERE product_id='xxx' LIMIT 100000, 20) b
WHERE a.id = b.id;

5. 架构相关

Q17:主从复制原理是什么?

详细内容见 主从复制

答案:基于 binlog 的三个线程:

  1. 主库 binlog dump 线程:读取 binlog 发送给从库
  2. 从库 IO 线程:拉取 binlog 写入 relay log
  3. 从库 SQL 线程:回放 relay log

Q18:主从延迟怎么处理?

答案

  1. 关键读走主库
  2. 判断 Seconds_Behind_Master
  3. 写完后 1-2 秒内的读走主库
  4. 用 GTID 检查从库是否已回放到指定位置

Q19:读写分离怎么做?

答案

6. 其他高频题

Q20:char 和 varchar 的区别?

对比 char varchar
长度 固定长度 可变长度
存储 不足补空格 只存实际数据
性能 定长,更快 变长,需要记录长度
适用场景 长度固定的字段(如手机号) 长度变化大的字段

Q21:count(*)、count(1)、count(列名) 有什么区别?

写法 说明
COUNT(*) 统计所有行,包括 NULL
COUNT(1) 统计所有行,和 COUNT(*) 效果一样
COUNT(列名) 只统计该列非 NULL 的行

InnoDB 下 COUNT(*)COUNT(1) 性能差不多,优化器会自动选择最优方式。

Q22:为什么建议用自增主键?

详细内容见 页分裂

答案

  1. 自增主键总是最大的,插入时追加到最后一个页,不用分裂
  2. 整型比 UUID 小(4/8 字节 vs 36 字节),索引树更矮
  3. 顺序存储,范围查询性能好

Q23:truncate 和 delete 的区别?

对比 DELETE TRUNCATE
类型 DML DDL
删除方式 逐行删除 直接释放数据页
日志 记录每行删除 只记录页释放
速度 慢(大表)
回滚 可以回滚 不能回滚
自增 ID 不重置 重置为初始值