Skip to content

MySQL 8.0 面试题精选 ​

生成时间:2026-06-07 22:58 | 共 10 题


1. InnoDB 的内存架构由哪几部分组成?Buffer Pool 的淘汰策略是什么? ​

难度: ⭐⭐

标准答案:

InnoDB 内存架构主要由三大块组成:

  1. Buffer Pool(缓冲池):缓存数据页和索引页,是 InnoDB 最核心的内存区域。采用改良的 LRU 算法,将 LRU 链表分为 young 区(热数据,前 5/8) 和 old 区(冷数据,后 3/8)。新读取的页先放入 old 区头部,如果在 innodb_old_blocks_time(默认 1 秒)内再次被访问,才会移入 young 区头部,避免全表扫描污染热数据。
  2. Log Buffer(日志缓冲):缓存 redo log,按 innodb_log_buffer_size 配置大小,定期刷入 redo log 文件。
  3. Additional Memory Pool:InnoDB 8.0 已移除该结构,内存管理统一交给操作系统。

Buffer Pool 淘汰策略:young 区尾部页面被淘汰。页面从 old → young 的晋升条件是存活超过 innodb_old_blocks_time 后再次访问。

延伸思考: MySQL 8.0 支持多 Buffer Pool 实例(innodb_buffer_pool_instances),高并发场景下减少锁竞争。如何根据服务器内存合理配置 Buffer Pool 大小和实例数?


2. MySQL 8.0 的四种事务隔离级别分别是什么?各自如何解决并发问题? ​

难度: ⭐⭐

标准答案:

隔离级别脏读不可重复读幻读实现方式
READ UNCOMMITTED❌❌❌无限制
READ COMMITTED (RC)✅❌❌MVCC + 每次 SELECT 生成新 Read View
REPEATABLE READ (RR)✅✅部分解决MVCC + 事务首次 SELECT 生成 Read View
SERIALIZABLE✅✅✅所有 SELECT 隐式加共享锁

MySQL 8.0 默认隔离级别为 REPEATABLE READ。RR 级别通过 MVCC 解决普通读的幻读问题,但当前读(SELECT ... FOR UPDATE)仍可能幻读,需配合 Gap Lock / Next-Key Lock 才能完全避免。

延伸思考: 为什么 MySQL 的 RR 级别不能完全避免幻读?什么场景下 RR 仍会出现幻读?如何通过加锁彻底避免?


3. 详细解释 InnoDB 的 MVCC 机制,包括隐藏字段、Undo Log 和 Read View 的协作过程 ​

难度: ⭐⭐⭐

标准答案:

MVCC(多版本并发控制)依赖三个核心组件:

隐藏字段(每行记录):

  • DB_TRX_ID:最后修改该行的事务 ID
  • DB_ROLL_PTR:回滚指针,指向 Undo Log 中该行的上一个版本
  • DB_ROW_ID:隐藏主键(无显式主键时自动生成)

Undo Log 版本链: 每次修改都通过 DB_ROLL_PTR 串联成链表,从最新版本指向最旧版本。

Read View 可见性判断(RC 每次查询新建,RR 事务内复用):

  • m_ids:生成 Read View 时所有活跃事务 ID 列表
  • min_trx_id:活跃事务最小 ID
  • max_trx_id:下一个将分配的事务 ID
  • creator_trx_id:创建该 Read View 的事务 ID

可见性规则:

  1. DB_TRX_ID == creator_trx_id → 可见(自己修改的)
  2. DB_TRX_ID < min_trx_id → 可见(事务已提交)
  3. DB_TRX_ID >= max_trx_id → 不可见(事务在 Read View 之后开始)
  4. min_trx_id <= DB_TRX_ID < max_trx_id → 若在 m_ids 中则不可见(事务未提交),否则可见

不可见时沿 Undo Log 版本链回溯,直到找到可见版本。

延伸思考: RC 和 RR 的核心差异仅在 Read View 的生成时机。长事务在 RR 下持有旧 Read View 会导致什么问题?Undo Log 膨胀如何影响系统性能?


4. InnoDB 的锁机制有哪些?行锁、间隙锁、临键锁和意向锁各自的作用和使用场景是什么? ​

难度: ⭐⭐⭐

标准答案:

行级锁:

  • Record Lock(记录锁):锁定索引记录本身,分为 S Lock(共享)和 X Lock(排他)
  • Gap Lock(间隙锁):锁定索引记录之间的间隙,仅用于防止插入,不与其他 Gap Lock 冲突。RR 级别下生效,RC 下失效
  • Next-Key Lock(临键锁):Record Lock + Gap Lock 的组合,左开右闭区间,是 InnoDB 在 RR 下的默认行锁算法

表级锁:

  • 意向锁(Intention Lock):IS / IX,表级标记,表示事务打算对表中行加 S/X 锁。意向锁之间互不阻塞,仅用于快速判断表级锁与行级锁是否冲突(如表级 X Lock 与行级 IX 冲突)
  • AUTO-INC Lock:自增列插入时的表级锁

锁兼容矩阵(行级):

S RecordX Record
S Record✅❌
X Record❌❌

Gap Lock 不冲突 Gap Lock,只冲突 Insert 操作。

延伸思考: 为什么 InnoDB 选择 Next-Key Lock 作为 RR 下的默认算法?在高并发插入场景下,Gap Lock 可能导致哪些死锁问题?


5. 如何优化一条慢查询 SQL?请描述完整的分析流程 ​

难度: ⭐⭐

标准答案:

完整优化流程:

  1. 开启慢查询日志:slow_query_log = ON,long_query_time = 1
  2. 定位慢 SQL:通过 mysqldumpslow 或 pt-query-digest 分析
  3. EXPLAIN 分析执行计划,重点关注:
    • type:访问类型(system > const > eq_ref > ref > range > index > ALL)
    • key:实际使用的索引
    • rows:预估扫描行数
    • Extra:Using index(覆盖索引✅)、Using filesort(❌)、Using temporary(❌)
  4. 优化策略:
    • 添加合适索引(最左前缀、覆盖索引、索引下推 ICP)
    • 避免索引失效:不在索引列上函数运算、隐式类型转换、LIKE '%xx'、OR 条件部分无索引
    • 减少回表:使用覆盖索引
    • MySQL 8.0 索引跳跃扫描(Index Skip Scan):复合索引首列无条件时也可利用
  5. Optimizer Trace:SET optimizer_trace='enabled=on' 查看优化器决策过程
  6. Profile 分析:SET profiling = 1 查看各阶段耗时

延伸思考: MySQL 8.0 的索引跳跃扫描在什么条件下生效?和 Oracle 的 Index Skip Scan 有何异同?


6. MySQL 8.0 主从复制的原理是什么?GTID 复制相比传统复制有哪些优势? ​

难度: ⭐⭐

标准答案:

复制原理(三线程模型):

  1. Binlog Dump Thread(主库):发送 binlog 事件给从库
  2. IO Thread(从库):接收 binlog 写入 Relay Log
  3. SQL Thread(从库):回放 Relay Log 中的事件

GTID(Global Transaction Identifier)复制优势:

特性传统复制GTID 复制
事务定位靠 binlog 文件名 + position靠全局唯一的 server_uuid:transaction_id
主从切换手动计算 binlog 位点自动定位,CHANGE MASTER TO MASTER_AUTO_POSITION=1
数据一致性校验困难天然支持,从库可验证 GTID 集合完整性
多源复制管理复杂每个源 GTID 集合独立

MySQL 8.0 还支持增强半同步复制(After Sync):主库先将 binlog 写入从库 Relay Log 后再提交本地事务,比 After Commit 更安全。

延伸思考: 主从延迟如何监控和解决?并行复制(基于组提交的并行、基于 writeset 的并行)的原理和适用场景?


7. 分库分表的策略有哪些?如何处理分库分表后的跨库查询和分布式事务? ​

难度: ⭐⭐⭐

标准答案:

分片策略:

  • 水平分表:按行拆分(范围分片、Hash 分片、一致性 Hash)
  • 垂直分库:按业务拆分(用户库、订单库、商品库)
  • 垂直分表:将大表的冷热字段分离

分片键选择原则: 高基数、查询频繁、数据分布均匀、避免跨片查询

跨库查询方案:

  1. 全局表(广播表):小表每个库都存一份(如字典表)
  2. ER 分片:关联表按相同分片键分到同一库
  3. 应用层 Join:多次查询后在内存中拼接
  4. 中间件支持:ShardingSphere 等支持跨库 Join(性能较差)

分布式事务方案:

  1. 2PC / XA 事务:MySQL 原生支持 XA,但存在同步阻塞问题
  2. TCC(Try-Confirm-Cancel):业务层面实现柔性事务
  3. Saga 模式:长事务拆分为多个本地事务 + 补偿操作
  4. Seata AT 模式:拦截 SQL 自动生成回滚日志

延伸思考: 分库分表后分页查询如何优化?深分页(limit 100000, 10)在不同分片策略下有什么不同的解法?


8. MySQL 8.0 新增的窗口函数有哪些?与聚合函数有什么本质区别?请举例说明 ​

难度: ⭐⭐

标准答案:

本质区别: 聚合函数将多行合并为一行;窗口函数对每行计算,保留原始行数,基于"窗口"(行集合)计算。

常用窗口函数分类:

类别函数
排序ROW_NUMBER(), RANK(), DENSE_RANK()
取值LEAD(), LAG(), FIRST_VALUE(), LAST_VALUE(), NTH_VALUE()
聚合SUM(), AVG(), COUNT(), MAX(), MIN()(加 OVER 子句)
分布PERCENT_RANK(), CUME_DIST(), NTILE()

示例 — 计算每个部门薪水排名:

sql
SELECT 
    dept_id,
    emp_name,
    salary,
    ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn,
    RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rnk,
    DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS drnk
FROM employees;

ROW_NUMBER vs RANK vs DENSE_RANK: 并列时 RANK 跳号(1,1,3),DENSE_RANK 不跳号(1,1,2),ROW_NUMBER 不并列(1,2,3)。

窗口帧语法: ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 定义计算范围。

延伸思考: 如何用窗口函数高效实现"同组 Top N"查询?对比传统自连接写法的性能差异。


9. MySQL 8.0 的 CTE(公共表表达式)和递归 CTE 如何使用?适用场景有哪些? ​

难度: ⭐⭐

标准答案:

非递归 CTE: 语义上类似派生表(子查询),但可复用、可读性更强。

sql
WITH dept_avg AS (
    SELECT dept_id, AVG(salary) AS avg_sal
    FROM employees
    GROUP BY dept_id
)
SELECT e.emp_name, e.salary, d.avg_sal
FROM employees e
JOIN dept_avg d ON e.dept_id = d.dept_id
WHERE e.salary > d.avg_sal;

递归 CTE: 必须有终止条件,适用于层级/树形结构查询。

sql
WITH RECURSIVE emp_chain AS (
    -- 锚点成员:顶层经理
    SELECT emp_id, emp_name, manager_id, 1 AS level
    FROM employees
    WHERE manager_id IS NULL
    UNION ALL
    -- 递归成员:逐层向下
    SELECT e.emp_id, e.emp_name, e.manager_id, ec.level + 1
    FROM employees e
    JOIN emp_chain ec ON e.manager_id = ec.emp_id
)
SELECT * FROM emp_chain ORDER BY level;

适用场景:

  • 组织架构树、评论嵌套、菜单层级
  • 生成序列数据(日期序列、数字序列)
  • 图遍历(最短路径等)
  • 替代复杂的多层嵌套子查询

注意: cte_max_recursion_depth 默认 1000,超限报错需调整。

延伸思考: 递归 CTE 和存储过程递归的优劣对比?大数据量下递归 CTE 的性能瓶颈在哪里?


10. MySQL 8.0 的 JSON 支持有哪些新特性?如何为 JSON 字段创建高效索引? ​

难度: ⭐⭐

标准答案:

MySQL 8.0 JSON 新特性:

  1. JSON_TABLE():将 JSON 数组展开为关系表,是 8.0 最重要的 JSON 函数

    sql
    SELECT jt.name, jt.age
    FROM users,
    JSON_TABLE(json_col, '$.persons[*]' COLUMNS (
        name VARCHAR(50) PATH '$.name',
        age INT PATH '$.age'
    )) AS jt;
  2. ->> 运算符:内联路径提取 + 自动去引号(等价于 JSON_UNQUOTE(JSON_EXTRACT()))

  3. JSON 聚合函数:JSON_ARRAYAGG()、JSON_OBJECTAGG()

  4. JSON 修改函数:JSON_SET()、JSON_INSERT()、JSON_REPLACE()、JSON_REMOVE()

  5. JSON Merge Patch:JSON_MERGE_PATCH()(RFC 7396)

JSON 索引方案:

JSON 列无法直接建索引,需通过以下方式间接索引:

  1. Generated Column + 索引(推荐):

    sql
    ALTER TABLE users ADD COLUMN city VARCHAR(50)
      GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json_col, '$.city'))) STORED,
    ADD INDEX idx_city (city);
  2. MySQL 8.0.17+ 多值索引:

    sql
    CREATE INDEX idx_tags ON users (
      (CAST(json_col->'$.tags' AS CHAR(32) ARRAY))
    );
    -- 支持 MEMBER OF / JSON_CONTAINS / JSON_OVERLAPS
    SELECT * FROM users WHERE 'mysql' MEMBER OF(json_col->'$.tags');

延伸思考: 何时该用 JSON 字段而非传统关系建模?JSON 字段的查询性能瓶颈和最佳实践是什么?多值索引的适用场景和限制?


📌 复习建议: 优先掌握 MVCC(题3)、锁机制(题4)、SQL优化(题5),这三个是面试高频核心。窗口函数和 CTE 是 8.0 新特性亮点,了解即可加分。