MySQL 8.0 面试题集锦
生成时间:2026-06-07 22:38(北京时间)
覆盖主题:InnoDB架构、索引优化、事务隔离、MVCC、锁机制、SQL优化、主从复制、分库分表、窗口函数、CTE、JSON支持、性能调优
第1题:InnoDB 的多版本并发控制(MVCC)是如何工作的?
难度: ⭐⭐⭐
标准答案:
InnoDB 的 MVCC(Multi-Version Concurrency Control)通过 隐藏列 + undo log 实现:
- 每行数据有两个隐藏列:
DB_TRX_ID(最近修改的事务ID)和DB_ROLL_PTR(指向 undo log 记录的指针) - 每次 UPDATE 时,旧数据被写入 undo log,新数据写入数据页,
DB_ROLL_PTR指向旧版本 - 读取时,根据事务的
read view(快照)判断:若行的DB_TRX_ID对事务可见,或该版本已被删除,则通过DB_ROLL_PTR链式回溯查找可见的历史版本
关键点: READ COMMITTED 和 REPEATABLE READ 的区别在于 read view 的生成时机——前者每条 SQL 重新生成,后者事务开始时生成。
第2题:InnoDB 行锁的种类有哪些?间隙锁(Gap Lock)的作用是什么?
难度: ⭐⭐
标准答案:
InnoDB 行锁分为:
| 类型 | 说明 |
|---|---|
| 记录锁(Record Lock) | 锁住索引记录本身 |
| 间隙锁(Gap Lock) | 锁住索引记录之间的间隙,防止幻读 |
| 临键锁(Next-Key Lock) | 记录锁 + 间隙锁的组合,REPEATABLE READ 下的默认加锁方式 |
| 插入意向锁(Insert Intention Lock) | INSERT 操作在等待时获取的间隙锁,告诉其他事务"有线程正在等待插入" |
间隙锁的作用: 在 RR 隔离级别下,防止其他事务在范围内插入新记录,避免幻读(Phantom Read)。
延伸思考: 间隙锁只在唯一索引的等值查询时退化(退化为记录锁),非唯一索引的等值查询和范围查询仍会加间隙锁,这是导致死锁的常见原因。
第3题:MySQL 8.0 的 CTE(公用表表达式)相比传统子查询有什么优势?
难度: ⭐⭐
标准答案:
CTE(Common Table Expression)分为普通 CTE 和递归 CTE:
-- 普通 CTE:替代重复子查询,语义更清晰
WITH active_users AS (
SELECT * FROM users WHERE status = 'active'
)
SELECT u.* FROM active_users u
JOIN orders o ON u.id = o.user_id;
-- 递归 CTE:替代 WITH RECURSIVE,语法更简洁
WITH RECURSIVE cte AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM cte WHERE n < 10
)
SELECT * FROM cte;优势:
- 可读性更高——将复杂逻辑命名提取,主查询更简洁
- 可重复引用——同一 CTE 在查询中可被多次使用
- 递归能力更强——替代了旧的
WITH RECURSIVE语法,支持更直观的树/层级结构查询 - 适合临时数据分析,无需创建临时表
第4题:REPEATABLE READ 隔离级别下,如何解决幻读问题?
难度: ⭐⭐⭐
标准答案:
MySQL InnoDB 在 RR 级别下通过以下机制解决幻读:
- MVCC 解决快照读的幻读:事务的 read view 不变,避免看到其他事务新提交的插入
- Next-Key Lock 解决当前读的幻读:对扫描到的索引范围加锁(记录锁 + 间隙锁),阻止其他事务在范围内插入
但存在例外: SELECT ... FOR UPDATE 和 INSERT 操作仍可能触发幻读(Next-Key Lock 仅在索引覆盖范围内生效)。极端场景可通过 LOCK IN SHARE MODE 或显式加锁(SELECT ... FOR UPDATE WHERE pk = xxx)强化控制。
延伸思考: 淘宝等高并发业务通常将隔离级别降为 READ COMMITTED,放弃间隙锁以换取并发性能,幻读通过业务层兜底。
第5题:Explain 输出中 type、key、Extra 字段有哪些重要值需要关注?
难度: ⭐⭐
标准答案:
type 字段(连接类型,从好到差):
system > const > eq_ref > ref > range > index > ALLconst:主键/唯一索引等值查询,最多返回1条记录ref:非唯一索引等值查询range:索引范围扫描(>, <, BETWEEN, IN, LIKE 前缀)ALL:全表扫描(必须优化)
key 字段: 实际使用的索引名,NULL 表示未走索引。
Extra 字段关键值:
| 值 | 含义 |
|---|---|
| Using index | 覆盖索引,无需回表 |
| Using index condition | 索引下推(ICP) |
| Using where | 需要在存储引擎层后过滤 |
| Using filesort | 文件排序,需优化(加索引或改写 SQL) |
| Using temporary | 使用临时表,需优化 |
| Using join buffer | NL 连接时使用 join buffer |
第6题:什么是索引下推(Index Condition Pushdown,ICP)?MySQL 8.0 有哪些新优化?
难度: ⭐⭐
标准答案:
ICP 原理: 在 MySQL 5.6+引入,以前存储引擎读取数据到 server 层后再过滤条件;ICP 允许将条件下推到存储引擎层,在索引遍历时直接过滤,减少回表次数。
示例: 复合索引 (name, age),查询 WHERE name = '张三' AND age > 25
- 无 ICP:先按
name找到所有记录,回表后再过滤age - 有 ICP:存储引擎层直接用
age > 25过滤,只回表符合条件的记录
MySQL 8.0 新增优化:
- 倒排索引(Inverted Index):支持
JSON_FULLTEXT()对 JSON 字段的全文搜索 - 窗口函数原生支持(8.0+):无需模拟,直接使用
ROW_NUMBER()、RANK()、LAG()等 - Skip Scan:优化非前缀列的选择性查询(
SELECT COUNT(DISTINCT name) FROM orders可跳过 name 列直接扫描) - 函数索引:可直接对表达式建索引,如
INDEX idx (( YEAR(create_time) ))
第7题:MySQL 主从复制中,binlog 有哪三种格式?各自优缺点是什么?
难度: ⭐⭐
标准答案:
| 格式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| ROW | 记录每行数据的变化 | 数据一致性最高,可准确同步;支持闪电崩恢复 | binlog 体积最大,网络传输开销高 |
| STATEMENT | 记录修改数据的 SQL 语句 | binlog 最小,可审计;支持变量函数(如 NOW())的正确语义 | 某些函数执行结果不确定(如 UUID()),可能导致主从不一致 |
| MIXED | 混合模式,MySQL 5.7+默认 | 正常用 STATEMENT,危险语句自动切换为 ROW | 行为不可预测,排查问题较复杂 |
建议: 金融类一致性要求高的场景用 ROW;高并发日志类场景用 STATEMENT;一般场景用 MIXED。
补充: MySQL 8.0 支持 binlog_transaction_compression,可对 binlog 事务级压缩,节省 60%~80% 传输流量。
第8题:分库分表的常见中间件有哪些?分片策略如何选择?
难度: ⭐⭐⭐
标准答案:
常见中间件: ShardingSphere(ShardingSphere-JDBC / ShardingSphere-Proxy)、MyCat、Vitess(Vitess 是 YouTube 开源的分布式 MySQL 方案)。
分片策略:
| 策略 | 适用场景 | 缺点 |
|---|---|---|
| 哈希分片 | 数据分布均匀 JOIN 少 | 扩缩容成本高(需要全量迁移) |
| 范围分片 | 按时间/ID顺序查询的场景 | 热点数据集中在单个分片 |
| 一致性哈希 | 减少扩缩容时数据迁移量 | 实现复杂,节点不均问题 |
跨分片查询的解决方案:
- 异构索引表:按查询字段再建一张路由表
- ES/Hive 离线查询:将数据同步到搜索引擎
- 广播表:小表全量复制到各分片
- 分布式事务(Seata / AT 模式):保证跨库一致性
第9题:窗口函数(Window Functions)和普通聚合函数的区别是什么?请举例
难度: ⭐⭐
标准答案:
普通聚合函数将多行压缩为一行,窗口函数不减少行数,每行仍然保留,同时附加聚合结果。
-- 普通聚合:10行 → 1行
SELECT SUM(salary) FROM employees;
-- 窗口函数:10行 → 10行,每行都有各自的汇总值
SELECT
name,
dept,
salary,
SUM(salary) OVER (PARTITION BY dept ORDER BY hire_date) AS dept_running_total,
AVG(salary) OVER (PARTITION BY dept) AS dept_avg,
RANK() OVER (ORDER BY salary DESC) AS salary_rank,
LAG(salary, 1) OVER (ORDER BY hire_date) AS prev_salary,
salary - AVG(salary) OVER (PARTITION BY dept) AS diff_from_dept_avg
FROM employees;常用窗口函数分类:
- 聚合类:
SUM()、AVG()、COUNT()、MAX()、MIN() - 排名类:
ROW_NUMBER()(无并列)、RANK()(有并列)、DENSE_RANK()(无间隔) - 导航类:
LAG()(前一行)、LEAD()(后一行)、FIRST_VALUE()、LAST_VALUE() - 分布类:
PERCENT_RANK()、CUME_DIST()、NTILE(n)
第10题:MySQL 8.0 的 JSON 数据类型有哪些常用函数?如何利用 JSON 索引优化查询?
难度: ⭐⭐
标准答案:
常用 JSON 函数:
-- 查询
JSON_EXTRACT(col, '$.key') -- 提取值(MySQL 8.0 可用 col->>'$.key' 简写)
JSON_CONTAINS(col, '"value"') -- 是否包含
JSON_SEARCH(col, 'one', '%key%') -- 搜索
-- 创建与修改
JSON_OBJECT('name', '张三', 'age', 30) -- 构建 JSON 对象
JSON_ARRAY('a', 'b', 'c') -- 构建 JSON 数组
JSON_SET(col, '$.gender', 'M') -- 新增/更新字段
JSON_INSERT(col, '$.addr', '北京') -- 仅插入(不覆盖已有)
JSON_REMOVE(col, '$.temp') -- 删除字段
JSON_MERGE_PATCH(col1, col2) -- 合并(后者覆盖前者)
-- 类型转换与检查
JSON_TYPE(col) -- 返回值的 JSON 类型
JSON_VALID(str) -- 检查是否为合法 JSONJSON 索引优化:
MySQL 8.0 支持表达式索引,对 JSON 字段中的某个路径建索引:
-- 创建表达式索引(虚拟列 + 索引)
ALTER TABLE orders ADD COLUMN city VARCHAR(50)
GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(addr, '$.city'))) VIRTUAL;
CREATE INDEX idx_city ON orders(city);
-- 等价于直接索引 addr 字段的 city 路径
CREATE INDEX idx_city ON orders((JSON_EXTRACT(addr, '$.city')));
-- 查询时需保持一致才能命中索引
SELECT * FROM orders WHERE JSON_EXTRACT(addr, '$.city') = '北京';性能建议: 频繁查询的 JSON 路径应建表达式索引;JSON 体积过大会影响性能,考虑冗余字段替代。
📚 本题库由阿虾 AI 助手自动生成 | 祝面试顺利!