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 关键优化:
- 多 Buffer Pool 实例:
innodb_buffer_pool_instances,减少锁争用,建议 Buffer Pool ≥ 1GB 时设为多个实例。 - Buffer Pool 预热:
innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,重启时恢复热数据。 - 在线调整大小:
innodb_buffer_pool_size可在线动态修改,8.0 优化了调整过程中的性能。 - 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 条件部分可下推 |
| Extra | Using index | Using 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 核心组件:
- 隐藏列:每行数据包含
DB_TRX_ID(最近修改的事务 ID)和DB_ROLL_PTR(回滚指针,指向 Undo Log 中的前一个版本)。 - Undo Log 版本链:每次 UPDATE 操作将旧值写入 Undo Log,通过
DB_ROLL_PTR串成版本链,形成该行的历史版本快照。 - 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 等)使用的是最新数据而非快照:
-- 事务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 分析加锁过程。
标准答案:
锁类型详解:
Record Lock(记录锁):锁定索引记录本身。只有在通过索引(主键或唯一索引)精确定位到一行时才使用。
SELECT * FROM t WHERE id = 5 FOR UPDATE→ 对 id=5 加 X Record Lock
Gap Lock(间隙锁):锁定索引记录之间的间隙,防止其他事务在间隙中插入新记录。Gap Lock 之间不互斥,仅与 Insert Intention Lock 冲突。
SELECT * FROM t WHERE id > 3 AND id < 8 FOR UPDATE→ 对 (3, 8) 间隙加 Gap Lock
Next-Key Lock(临键锁):= Gap Lock + Record Lock,锁定一个左开右闭区间
(前一个记录, 当前记录]。InnoDB 在 RR 级别下默认使用 Next-Key Lock 防止幻读。意向锁(Intention Lock):表级锁,表示事务打算对表中的行加行锁。分为意向共享锁(IS)和意向排他锁(IX)。
- 作用:快速判断表中是否存在行锁,避免逐行检查。例如事务要加表级 X 锁时,只需检查是否存在 IS/IX,而不用扫描每行。
- 兼容性:IS/IX 之间互相兼容,IS/IX 与表级 S/X 锁按规则互斥。
兼容性矩阵:
| IS | IX | S | X | |
|---|---|---|---|---|
| IS | ✅ | ✅ | ✅ | ❌ |
| IX | ✅ | ✅ | ❌ | ❌ |
| S | ✅ | ❌ | ✅ | ❌ |
| X | ❌ | ❌ | ❌ | ❌ |
加锁分析示例(RR 级别,id 为主键,值 1, 5, 10):
-- 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问题诊断:
- 全表扫描(type=ALL):没有使用任何索引,扫描 100 万行。
- 过滤率极低(filtered=1%):100 万行中只有约 1 万行满足条件,99% 的 I/O 浪费。
- 文件排序(Using filesort):无法利用索引排序,需额外排序操作。
优化思路:
- 添加合适的索引:根据 WHERE 条件和 ORDER BY 子句创建联合索引。如果 WHERE 条件为
status = 1,ORDER BY 为created_at DESC,则创建idx_status_created(status, created_at)。 - 覆盖索引:如果 SELECT 的列也能包含在索引中,避免回表。
- 优化排序:联合索引的顺序应让 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。
工作流程:
- 主库事务提交时,为每个事务分配唯一 GTID,写入 Binlog。
- 从库 I/O 线程拉取 Binlog,写入 Relay Log。
- 从库 SQL 线程回放时,记录已执行的 GTID 集合(
gtid_executed)。 - 自动定位:从库根据自身
gtid_executed与主库gtid_purged的差异,自动确定复制起点,无需手动指定位点和文件。
GTID vs 传统位点复制:
| 维度 | 传统位点复制 | GTID 复制 |
|---|---|---|
| 主从切换 | 需手动查找位点,易出错 | 自动定位,CHANGE MASTER TO MASTER_AUTO_POSITION=1 |
| 数据一致性 | 难以判断从库缺少哪些事务 | 通过 GTID 集合精确判断 |
| 主主复制 | 位点冲突风险 | GTID 全局唯一,无冲突 |
| 复制延迟监控 | 需对比位点值 | SHOW MASTER STATUS 对比 GTID 集合即可 |
MySQL 8.0 并行复制演进:
- Database 级别并行(5.6):不同 DB 的事务可并行,单 DB 内串行。实用性差。
- Group Commit 并行(5.7):
slave_parallel_type=LOGICAL_CLOCK,同一组提交的事务可并行。但并行度受主库组提交频率限制。 - WRITESET 并行(8.0):
binlog_transaction_dependency_tracking=WRITESET- 原理:为每行数据计算 Hash(基于主键 + 库名 + 表名),构成 WriteSet。如果两个事务的 WriteSet 没有交集(修改的行不重叠),则可以并行回放。
- 关键参数:
binlog_transaction_dependency_tracking = WRITESETtransaction_write_set_extraction = XXHASH64binlog_transaction_dependency_history_size(默认 25000,WriteSet 历史大小)
- 优势:不受组提交频率限制,单 DB 内无冲突的事务也能并行,大幅提升从库回放速度。
延伸思考: WRITESET 并行复制在什么场景下效果最好?什么场景下退化为与 LOGICAL_CLOCK 相同的并行度?(提示:热点行更新密集时 WriteSet 冲突率高,并行度下降。)
7. 分库分表:策略选型与跨分片查询
难度: ⭐⭐⭐
题目: 在什么情况下应该考虑分库分表?请对比水平分表和垂直分表的适用场景,并详细分析分库分表后跨分片查询(如分页排序、聚合统计、跨片 JOIN)的解决方案和取舍。
标准答案:
何时考虑分库分表:
- 单表数据量 > 2000 万行(经验值,B+Tree 层级从 3 变 4,性能断崖式下降)
- 单库 QPS 超过单机承载能力
- 慢查询无法通过索引优化解决
分片策略对比:
| 维度 | 垂直分表 | 水平分表 |
|---|---|---|
| 切分方式 | 按列拆分,不同列分布到不同表 | 按行拆分,相同结构分布到不同表 |
| 典型场景 | 大字段(TEXT/BLOB)拆出 | 单表行数过大 |
| 分库维度 | 按业务模块拆分到不同库 | 同一表数据按规则分布到多个库 |
水平分片键选择原则:
- 高频查询条件字段
- 数据分布均匀(避免热点分片)
- 尽量避免跨片查询
跨分片查询解决方案:
分页排序:
- 方案A:各分片执行查询 + 内存归并排序。深层分页(offset 大)性能极差。
- 方案B:禁止深分页,改用游标分页(基于分片键的
WHERE id > last_id ORDER BY id LIMIT N)。 - 方案C:用 ES/ClickHouse 做二级索引和排序,MySQL 仅做存储。
聚合统计:
- 方案A:各分片聚合后汇总。如 SUM:各分片求和再相加;AVG:各分片求 SUM 和 COUNT 后计算。
- 方案B:异步同步到 OLAP 引擎(ClickHouse/StarRocks)做统计分析。
跨片 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() 三种排名函数的区别,并使用窗口函数实现:按部门分组计算每个员工薪资在其部门内的排名,以及薪资累计总额(从高到低累加)。
标准答案:
窗口函数语法结构:
函数名() 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 实现:
-- 表: 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 语法:
WITH cte_name [(col1, col2, ...)] AS (
SELECT ...
)
SELECT ... FROM cte_name;非递归 CTE:
- 类似于派生表(子查询),但可被引用多次,可读性更好。
- 执行原理:MySQL 8.0 对非递归 CTE 采用"物化"策略(将结果写入内部临时表),多次引用时复用物化结果,避免重复计算。
递归 CTE 结构:
WITH RECURSIVE cte_name AS (
-- 锚点成员(非递归部分,初始化)
SELECT ... FROM ... WHERE ...
UNION ALL
-- 递归成员(引用自身)
SELECT ... FROM cte_name JOIN ... WHERE ...
)
SELECT ... FROM cte_name;执行流程:
- 执行锚点查询,得到初始结果集。
- 将初始结果作为输入,执行递归查询。
- 重复步骤2,直到递归查询返回空结果集。
- 合并所有结果(UNION ALL)。
组织架构树遍历实现:
-- 表: 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 的全部能力:
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) 性能调优排查步骤:
步骤一:定位慢查询
-- 确保慢查询日志开启
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步骤二:分析锁等待
-- 查看当前锁等待(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 状态
-- 查看死锁日志
SHOW ENGINE INNODB STATUS\G
-- 查看事务状态
SELECT * FROM sys.innodb_trx\G步骤四:Performance Schema 深度分析
-- 开启关键消费者
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 核心知识点。