Skip to content

MySQL 8.0 面试题精选 ​

生成时间:2026-06-07 23:08 | 共 10 题


1. InnoDB 架构:Buffer Pool 的核心作用与优化策略 ​

难度: ⭐⭐

题目: 请详细描述 InnoDB Buffer Pool 的架构设计,包括其内部数据结构(LRU List、Free List、Flush List)、改进的 LRU 算法如何解决传统 LRU 的问题,以及在 MySQL 8.0 中有哪些关键的 Buffer Pool 优化配置?

标准答案:

InnoDB Buffer Pool 是 InnoDB 存储引擎的核心内存区域,用于缓存数据页和索引页,减少磁盘 I/O。

内部数据结构:

  • LRU List:采用改进的分代式 LRU 算法,将链表分为 Young Region(热数据,前 5/8)和 Old Region(冷数据,后 3/8)。新读取的页首先插入 Old Region 的头部,而非传统 LRU 的链表头部。当 Old Region 的页在 innodb_old_blocks_time(默认 1000ms)内被再次访问,才将其移至 Young Region 头部。这有效避免了全表扫描等一次性读取将热数据淘汰的问题。
  • Free List:管理空闲页,数据库启动时所有页都在 Free List 上,被使用后移至 LRU List。
  • Flush List:管理脏页(已修改但未刷盘的页),按修改时间排序,Checkpoint 时从此链表刷盘。

MySQL 8.0 关键优化:

  1. 多 Buffer Pool 实例:innodb_buffer_pool_instances,减少锁争用,建议 Buffer Pool ≥ 1GB 时设为多个实例。
  2. Buffer Pool 预热:innodb_buffer_pool_dump_at_shutdown 和 innodb_buffer_pool_load_at_startup,重启时恢复热数据。
  3. 在线调整大小:innodb_buffer_pool_size 可在线动态修改,8.0 优化了调整过程中的性能。
  4. Chunk 机制:以 chunk 为单位分配内存(默认 128MB),避免大块内存分配失败。

延伸思考: 如果 Buffer Pool 命中率低于 95%,可能的原因有哪些?如何诊断和优化?(提示:SHOW ENGINE INNODB STATUS 中的 Buffer Pool hit rate、innodb_buffer_pool_size 配置、是否存在大量临时表操作等。)


2. 索引优化:覆盖索引与索引下推 ​

难度: ⭐⭐

题目: 请解释覆盖索引(Covering Index)和索引下推(Index Condition Pushdown, ICP)的原理及区别,并结合具体 SQL 示例说明它们如何减少回表次数、提升查询性能。

标准答案:

覆盖索引:

  • 原理:当查询所需的所有列都包含在联合索引中时,InnoDB 可以直接从索引中获取全部数据,无需回表(无需访问主键索引的数据页)。
  • 示例:
    sql
    -- 表: user(name, age, city), 联合索引: idx_name_age_city(name, age, city)
    SELECT name, age, city FROM user WHERE name = '张三';
    -- ✅ 覆盖索引:索引包含所有查询列,无需回表
    
    SELECT name, age, city, email FROM user WHERE name = '张三';
    -- ❌ 需要回表:email 不在索引中
  • EXPLAIN 中体现:Extra 列显示 Using index。

索引下推(ICP):

  • 原理:MySQL 5.6+ 引入,在联合索引扫描时,将 WHERE 条件中涉及索引列的部分下推到存储引擎层执行,减少回表次数。存储引擎在遍历索引时先根据下推条件过滤,只对满足条件的记录回表。
  • 示例:
    sql
    -- 联合索引: idx_name_age(name, age)
    SELECT * FROM user WHERE name LIKE '张%' AND age > 20;
    
    -- 无 ICP:存储引擎找到 name LIKE '张%' 的所有索引记录,
    --         逐条回表取出完整行,再由 Server 层过滤 age > 20
    -- 有 ICP:存储引擎在索引中直接判断 age > 20,
    --         只对同时满足两个条件的记录回表
  • EXPLAIN 中体现:Extra 列显示 Using index condition。

核心区别:

维度覆盖索引索引下推
目标完全避免回表减少回表次数
条件查询列必须在索引中WHERE 条件部分可下推
ExtraUsing indexUsing index condition
互斥性可与 ICP 共存不与覆盖索引同时出现(覆盖索引无需回表,ICP 无意义)

延伸思考: 在什么情况下 ICP 无法生效?(提示:子查询条件、存储函数调用、非索引列条件等。)


3. 事务隔离级别与 MVCC 机制 ​

难度: ⭐⭐⭐

题目: MySQL 8.0 默认的事务隔离级别是什么?请深入阐述 InnoDB 的 MVCC 机制如何实现 RC(Read Committed)和 RR(Repeatable Read)两种隔离级别,包括 Undo Log 版本链、ReadView 的创建时机与判断逻辑,以及 RR 级别下为何仍可能出现幻读。

标准答案:

MySQL 8.0 默认隔离级别为 Repeatable Read(RR)。

MVCC 核心组件:

  1. 隐藏列:每行数据包含 DB_TRX_ID(最近修改的事务 ID)和 DB_ROLL_PTR(回滚指针,指向 Undo Log 中的前一个版本)。
  2. Undo Log 版本链:每次 UPDATE 操作将旧值写入 Undo Log,通过 DB_ROLL_PTR 串成版本链,形成该行的历史版本快照。
  3. ReadView:事务执行快照读时创建的"可见性判断视图",包含:
    • m_ids:生成 ReadView 时当前所有活跃(未提交)事务的 ID 列表
    • min_trx_id:m_ids 中的最小值
    • max_trx_id:下一个将要分配的事务 ID(即当前最大事务 ID + 1)
    • creator_trx_id:创建该 ReadView 的事务 ID

可见性判断规则:

  • DB_TRX_ID == creator_trx_id → 当前事务自己的修改,可见
  • DB_TRX_ID < min_trx_id → 事务已提交,可见
  • DB_TRX_ID >= max_trx_id → 事务在 ReadView 创建后才开始,不可见
  • min_trx_id <= DB_TRX_ID < max_trx_id → 若在 m_ids 中则未提交,不可见;否则已提交,可见

RC vs RR 的核心区别:

隔离级别ReadView 创建时机效果
RC每次 SELECT 都创建新的 ReadView能看到其他事务已提交的最新数据
RR事务中第一次 SELECT 时创建,后续复用事务内多次读取结果一致

RR 下的幻读场景: 快照读通过 MVCC 避免幻读,但当前读(SELECT ... FOR UPDATE、UPDATE、DELETE 等)使用的是最新数据而非快照:

sql
-- 事务A(RR级别)
SELECT * FROM t WHERE id > 5;           -- 快照读,返回2行
-- 事务B
INSERT INTO t VALUES (8, 'new'); COMMIT;
-- 事务A
SELECT * FROM t WHERE id > 5;           -- 快照读,仍返回2行(MVCC保护)
UPDATE t SET name='x' WHERE id > 5;     -- 当前读,更新3行!
SELECT * FROM t WHERE id > 5;           -- 快照读,现在返回3行!幻读!

延伸思考: InnoDB 如何在 RR 级别下通过 Next-Key Lock 防止当前读的幻读?Gap Lock 和 Record Lock 各自扮演什么角色?


4. 锁机制:行锁、间隙锁、Next-Key Lock 与意向锁 ​

难度: ⭐⭐⭐

题目: 请详细解释 InnoDB 中行锁(Record Lock)、间隙锁(Gap Lock)、Next-Key Lock 和意向锁(Intention Lock)的概念、加锁规则、兼容性矩阵,并结合具体 SQL 分析加锁过程。

标准答案:

锁类型详解:

  1. Record Lock(记录锁):锁定索引记录本身。只有在通过索引(主键或唯一索引)精确定位到一行时才使用。

    • SELECT * FROM t WHERE id = 5 FOR UPDATE → 对 id=5 加 X Record Lock
  2. Gap Lock(间隙锁):锁定索引记录之间的间隙,防止其他事务在间隙中插入新记录。Gap Lock 之间不互斥,仅与 Insert Intention Lock 冲突。

    • SELECT * FROM t WHERE id > 3 AND id < 8 FOR UPDATE → 对 (3, 8) 间隙加 Gap Lock
  3. Next-Key Lock(临键锁):= Gap Lock + Record Lock,锁定一个左开右闭区间 (前一个记录, 当前记录]。InnoDB 在 RR 级别下默认使用 Next-Key Lock 防止幻读。

  4. 意向锁(Intention Lock):表级锁,表示事务打算对表中的行加行锁。分为意向共享锁(IS)和意向排他锁(IX)。

    • 作用:快速判断表中是否存在行锁,避免逐行检查。例如事务要加表级 X 锁时,只需检查是否存在 IS/IX,而不用扫描每行。
    • 兼容性:IS/IX 之间互相兼容,IS/IX 与表级 S/X 锁按规则互斥。

兼容性矩阵:

ISIXSX
IS✅✅✅❌
IX✅✅❌❌
S✅❌✅❌
X❌❌❌❌

加锁分析示例(RR 级别,id 为主键,值 1, 5, 10):

sql
-- Session 1
SELECT * FROM t WHERE id = 5 FOR UPDATE;
-- 加锁:id=5 的 X Record Lock(唯一索引等值匹配,退化为 Record Lock)

-- Session 1
SELECT * FROM t WHERE id > 3 AND id < 8 FOR UPDATE;
-- 加锁:(1, 5] Next-Key Lock + (5, 10) Gap Lock

-- Session 1
SELECT * FROM t WHERE id = 7 FOR UPDATE;
-- 加锁:(5, 10] Next-Key Lock(7不存在,不会退化为 Record Lock)

MySQL 8.0 加锁规则总结(丁奇总结的两大原则 + 两个优化 + 一个 bug):

  • 原则1:加锁的基本单位是 Next-Key Lock
  • 原则2:查找过程中访问到的对象才会加锁
  • 优化1:唯一索引等值匹配,Next-Key Lock 退化为 Record Lock
  • 优化2:向右遍历到最后一个不满足条件的值时,Next-Key Lock 退化为 Gap Lock

延伸思考: 在 RC 级别下,Gap Lock 是否生效?为什么 RC 级别不需要 Gap Lock?


5. SQL 优化:EXPLAIN 执行计划深度解读 ​

难度: ⭐⭐

题目: 请解释 MySQL 8.0 EXPLAIN 输出中 type、key、rows、filtered、Extra 等关键字段的含义,并分析以下场景的优化思路:type = ALL 且 rows = 1000000,filtered = 1%,Extra = Using where; Using filesort。

标准答案:

EXPLAIN 关键字段:

字段含义关注点
type访问类型,从优到差:system > const > eq_ref > ref > range > index > ALL避免 ALL
key实际使用的索引NULL 表示未用索引
rows预估扫描行数越小越好
filtered存储引擎返回数据在 Server 层过滤后的比例越高越好
Extra额外信息关注 Using filesort、Using temporary、Using index

场景分析:

type=ALL, rows=1000000, filtered=1%, Extra=Using where; Using filesort

问题诊断:

  1. 全表扫描(type=ALL):没有使用任何索引,扫描 100 万行。
  2. 过滤率极低(filtered=1%):100 万行中只有约 1 万行满足条件,99% 的 I/O 浪费。
  3. 文件排序(Using filesort):无法利用索引排序,需额外排序操作。

优化思路:

  1. 添加合适的索引:根据 WHERE 条件和 ORDER BY 子句创建联合索引。如果 WHERE 条件为 status = 1,ORDER BY 为 created_at DESC,则创建 idx_status_created(status, created_at)。
  2. 覆盖索引:如果 SELECT 的列也能包含在索引中,避免回表。
  3. 优化排序:联合索引的顺序应让 ORDER BY 列也能利用索引的有序性,消除 filesort。

优化后可能的 EXPLAIN:

type=ref, key=idx_status_created, rows=10000, filtered=100%, Extra=Using index

延伸思考: Using filesort 一定代表性能差吗?什么情况下 filesort 是可接受的?如何判断 filesort 使用了内存还是磁盘?(提示:sort_buffer_size、max_length_for_sort_data 参数。)


6. 主从复制:GTID 复制与并行复制 ​

难度: ⭐⭐⭐

题目: 请阐述 MySQL 8.0 中基于 GTID 的主从复制原理,与传统基于位点的复制相比有哪些优势?并详细说明 MySQL 8.0 的多线程并行复制(基于 WRITESET 的并行复制)如何提升从库回放性能。

标准答案:

GTID 复制原理:

GTID(Global Transaction Identifier)= source_id:transaction_id,如 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5。

工作流程:

  1. 主库事务提交时,为每个事务分配唯一 GTID,写入 Binlog。
  2. 从库 I/O 线程拉取 Binlog,写入 Relay Log。
  3. 从库 SQL 线程回放时,记录已执行的 GTID 集合(gtid_executed)。
  4. 自动定位:从库根据自身 gtid_executed 与主库 gtid_purged 的差异,自动确定复制起点,无需手动指定位点和文件。

GTID vs 传统位点复制:

维度传统位点复制GTID 复制
主从切换需手动查找位点,易出错自动定位,CHANGE MASTER TO MASTER_AUTO_POSITION=1
数据一致性难以判断从库缺少哪些事务通过 GTID 集合精确判断
主主复制位点冲突风险GTID 全局唯一,无冲突
复制延迟监控需对比位点值SHOW MASTER STATUS 对比 GTID 集合即可

MySQL 8.0 并行复制演进:

  1. Database 级别并行(5.6):不同 DB 的事务可并行,单 DB 内串行。实用性差。
  2. Group Commit 并行(5.7):slave_parallel_type=LOGICAL_CLOCK,同一组提交的事务可并行。但并行度受主库组提交频率限制。
  3. WRITESET 并行(8.0):binlog_transaction_dependency_tracking=WRITESET
    • 原理:为每行数据计算 Hash(基于主键 + 库名 + 表名),构成 WriteSet。如果两个事务的 WriteSet 没有交集(修改的行不重叠),则可以并行回放。
    • 关键参数:
      • binlog_transaction_dependency_tracking = WRITESET
      • transaction_write_set_extraction = XXHASH64
      • binlog_transaction_dependency_history_size(默认 25000,WriteSet 历史大小)
    • 优势:不受组提交频率限制,单 DB 内无冲突的事务也能并行,大幅提升从库回放速度。

延伸思考: WRITESET 并行复制在什么场景下效果最好?什么场景下退化为与 LOGICAL_CLOCK 相同的并行度?(提示:热点行更新密集时 WriteSet 冲突率高,并行度下降。)


7. 分库分表:策略选型与跨分片查询 ​

难度: ⭐⭐⭐

题目: 在什么情况下应该考虑分库分表?请对比水平分表和垂直分表的适用场景,并详细分析分库分表后跨分片查询(如分页排序、聚合统计、跨片 JOIN)的解决方案和取舍。

标准答案:

何时考虑分库分表:

  • 单表数据量 > 2000 万行(经验值,B+Tree 层级从 3 变 4,性能断崖式下降)
  • 单库 QPS 超过单机承载能力
  • 慢查询无法通过索引优化解决

分片策略对比:

维度垂直分表水平分表
切分方式按列拆分,不同列分布到不同表按行拆分,相同结构分布到不同表
典型场景大字段(TEXT/BLOB)拆出单表行数过大
分库维度按业务模块拆分到不同库同一表数据按规则分布到多个库

水平分片键选择原则:

  1. 高频查询条件字段
  2. 数据分布均匀(避免热点分片)
  3. 尽量避免跨片查询

跨分片查询解决方案:

  1. 分页排序:

    • 方案A:各分片执行查询 + 内存归并排序。深层分页(offset 大)性能极差。
    • 方案B:禁止深分页,改用游标分页(基于分片键的 WHERE id > last_id ORDER BY id LIMIT N)。
    • 方案C:用 ES/ClickHouse 做二级索引和排序,MySQL 仅做存储。
  2. 聚合统计:

    • 方案A:各分片聚合后汇总。如 SUM:各分片求和再相加;AVG:各分片求 SUM 和 COUNT 后计算。
    • 方案B:异步同步到 OLAP 引擎(ClickHouse/StarRocks)做统计分析。
  3. 跨片 JOIN:

    • 方案A:小表广播(每个分片放一份小表副本),将跨片 JOIN 变为分片内 JOIN。
    • 方案B:应用层组装:分别查询后内存 JOIN。
    • 方案C:冗余字段:将 JOIN 需要的字段冗余到主表中,避免 JOIN。
    • 方案D:使用中间件(ShardingSphere、Vitess)的联邦查询能力。

延伸思考: 分布式事务(XA、Seata AT/TCC 模式)在分库分表场景下的适用性和性能影响?柔性事务(最终一致性)如何作为替代方案?


8. 窗口函数:排名与累计计算 ​

难度: ⭐⭐

题目: 请解释 MySQL 8.0 窗口函数的语法结构,对比 ROW_NUMBER()、RANK()、DENSE_RANK() 三种排名函数的区别,并使用窗口函数实现:按部门分组计算每个员工薪资在其部门内的排名,以及薪资累计总额(从高到低累加)。

标准答案:

窗口函数语法结构:

sql
函数名() OVER (
  [PARTITION BY 分组列]
  [ORDER BY 排序列 [ASC|DESC]]
  [frame_clause]  -- 窗口帧定义
)

三种排名函数对比:

函数特点示例(薪资: 9000, 8000, 8000, 7000)
ROW_NUMBER()连续编号,不重复1, 2, 3, 4
RANK()并列跳号1, 2, 2, 4
DENSE_RANK()并列不跳号1, 2, 2, 3

SQL 实现:

sql
-- 表: employees(id, name, dept, salary)

SELECT
  id,
  name,
  dept,
  salary,
  -- 部门内薪资排名(并列跳号)
  RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dept_rank,
  -- 部门内薪资排名(并列不跳号)
  DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dept_dense_rank,
  -- 部门内薪资累计(从高到低累加)
  SUM(salary) OVER (
    PARTITION BY dept
    ORDER BY salary DESC
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS dept_cumulative_salary
FROM employees
ORDER BY dept, salary DESC;

窗口帧(Frame)说明:

  • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:从分区的第一行累加到当前行。
  • RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:默认帧,按值范围而非物理行计算,排序值相同的行视为同一帧。
  • 对于累计求和,ROWS 和 RANGE 在有并列值时行为不同:ROWS 是逐行累加,RANGE 是同值行一起累加。

延伸思考: 如何使用窗口函数实现"取每个部门薪资 Top 3"的查询?MySQL 8.0 中能否直接在 WHERE 子句中引用窗口函数结果?为什么?(提示:窗口函数在 WHERE 之后执行,需用 CTE 或子查询包装。)


9. CTE(公用表表达式)与递归查询 ​

难度: ⭐⭐

题目: 请解释 MySQL 8.0 中 CTE(Common Table Expression)的语法和执行原理,包括非递归 CTE 和递归 CTE。使用递归 CTE 实现一个组织架构树的遍历:从指定员工开始,向下查找所有下属(包含多级),并标注每个下属的层级深度。

标准答案:

CTE 语法:

sql
WITH cte_name [(col1, col2, ...)] AS (
  SELECT ...
)
SELECT ... FROM cte_name;

非递归 CTE:

  • 类似于派生表(子查询),但可被引用多次,可读性更好。
  • 执行原理:MySQL 8.0 对非递归 CTE 采用"物化"策略(将结果写入内部临时表),多次引用时复用物化结果,避免重复计算。

递归 CTE 结构:

sql
WITH RECURSIVE cte_name AS (
  -- 锚点成员(非递归部分,初始化)
  SELECT ... FROM ... WHERE ...
  UNION ALL
  -- 递归成员(引用自身)
  SELECT ... FROM cte_name JOIN ... WHERE ...
)
SELECT ... FROM cte_name;

执行流程:

  1. 执行锚点查询,得到初始结果集。
  2. 将初始结果作为输入,执行递归查询。
  3. 重复步骤2,直到递归查询返回空结果集。
  4. 合并所有结果(UNION ALL)。

组织架构树遍历实现:

sql
-- 表: employees(id, name, manager_id)
-- manager_id 为上级员工 ID,顶级员工 manager_id 为 NULL

WITH RECURSIVE subordinates AS (
  -- 锚点:从指定员工开始
  SELECT
    id,
    name,
    manager_id,
    1 AS depth
  FROM employees
  WHERE id = 100  -- 起始员工ID

  UNION ALL

  -- 递归:查找下一级下属
  SELECT
    e.id,
    e.name,
    e.manager_id,
    s.depth + 1 AS depth
  FROM employees e
  INNER JOIN subordinates s ON e.manager_id = s.id
)
SELECT
  id,
  name,
  manager_id,
  depth,
  REPEAT('  ', depth - 1) || name AS tree_display
FROM subordinates
ORDER BY depth, id;

关键参数:

  • cte_max_recursion_depth(默认 1000):限制递归深度,防止无限递归。
  • SET SESSION cte_max_recursion_depth = 10000; 可调大限制。

延伸思考: 递归 CTE 能否实现"向上遍历"(从叶子节点到根节点)?如何实现?递归 CTE 在大数据量场景下有哪些性能陷阱?(提示:每次递归都是全量扫描上一次结果,深度大时效率低;可考虑应用层分批查询。)


10. JSON 支持与性能调优综合 ​

难度: ⭐⭐⭐

题目: MySQL 8.0 对 JSON 类型做了哪些增强?请说明以下内容:(1) JSON 列的存储格式与索引策略;(2) JSON_TABLE() 函数的使用场景;(3) 如何通过 performance_schema 和 sys 库定位慢查询和锁等待问题?给出具体的排查步骤和 SQL。

标准答案:

(1) JSON 存储与索引:

存储格式:MySQL 8.0 使用部分原地更新(Partial In-Place Update)优化 JSON 修改。对于 JSON_SET、JSON_REPLACE 等操作,如果修改后的 JSON 文档可以原地容纳(不超出原空间),则避免整文档重写。

索引策略:

  • JSON 列不能直接建索引(整文档索引无意义)
  • Generated Column + 索引:
    sql
    ALTER TABLE users ADD COLUMN city VARCHAR(50)
      GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(info, '$.city'))) STORED,
      ADD INDEX idx_city(city);
  • MySQL 8.0.17+ Multi-Valued Index(多值索引):
    sql
    CREATE INDEX idx_tags ON users(
      (CAST(JSON_EXTRACT(info, '$.tags') AS CHAR(32) ARRAY))
    );
    -- 支持 JSON 数组元素索引,查询: WHERE JSON_CONTAINS(info->'$.tags', '"mysql"')

(2) JSON_TABLE() 函数:

将 JSON 数组展开为关系表,使得可以对 JSON 数据使用 SQL 的全部能力:

sql
SELECT jt.name, jt.score
FROM students s,
     JSON_TABLE(
       s.courses,
       '$[*]' COLUMNS(
         name VARCHAR(50) PATH '$.name',
         score INT PATH '$.score'
       )
     ) AS jt
WHERE jt.score >= 90;

适用场景:JSON 数组数据需要与关系表 JOIN、聚合、过滤时。

(3) 性能调优排查步骤:

步骤一:定位慢查询

sql
-- 确保慢查询日志开启
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';

-- 通过 sys 库快速查看 Top N 慢查询
SELECT * FROM sys.statements_with_runtimes_in_95th_percentile\G

-- 查看最近执行的慢 SQL
SELECT * FROM sys.statements_with_full_table_scans\G

步骤二:分析锁等待

sql
-- 查看当前锁等待(8.0 新增的 data_locks / data_lock_waits)
SELECT
  dl.OBJECT_NAME,
  dl.LOCK_TYPE,
  dl.LOCK_MODE,
  dl.LOCK_STATUS,
  dl.LOCK_DATA,
  dw.REQUESTING_THREAD_ID,
  dw.REQUESTING_QUERY
FROM performance_schema.data_locks dl
LEFT JOIN performance_schema.data_lock_waits dw
  ON dl.ENGINE_TRANSACTION_ID = dw.BLOCKING_ENGINE_TRANSACTION_ID
WHERE dl.LOCK_STATUS = 'GRANTED';

-- sys 库简化查询
SELECT * FROM sys.innodb_lock_waits\G

步骤三:分析 InnoDB 状态

sql
-- 查看死锁日志
SHOW ENGINE INNODB STATUS\G

-- 查看事务状态
SELECT * FROM sys.innodb_trx\G

步骤四:Performance Schema 深度分析

sql
-- 开启关键消费者
UPDATE performance_schema.setup_consumers
  SET ENABLED = 'YES' WHERE NAME IN ('events_waits_current', 'events_statements_history_long');

-- 查看文件 I/O 热点
SELECT * FROM sys.io_global_by_file_by_bytes LIMIT 10;

-- 查看内存使用
SELECT * FROM sys.memory_global_by_current_bytes LIMIT 10;

延伸思考: 如何在生产环境中安全地使用 performance_schema?开启所有消费者对性能有多大影响?(提示:performance_schema 内存预分配,不动态增长;但历史表消费者会增加内存占用和 CPU 开销,建议按需开启。)


本文档覆盖 InnoDB 架构、索引优化、事务隔离、MVCC、锁机制、SQL 优化、主从复制、分库分表、窗口函数、CTE、JSON 支持与性能调优等 MySQL 8.0 核心知识点。