欢迎光临

MySQL 8.0 Performance Schema与慢查询监控深度实战:从指标采集到问题诊断全流程指南

数据库性能问题排查是每个DBA和后端工程师的日常挑战。MySQL 8.0 提供了强大的 Performance Schema 和慢查询日志机制,它们是从”数据库变慢了”到”精确定位到某条SQL、某个锁、某次IO等待”的关键工具链。本文将从架构原理讲起,覆盖配置方法、核心查询、sys schema快捷视图、以及多个真实排障场景,帮助你在生产环境中建立完整的性能诊断工作流。

一、Performance Schema 架构与核心概念

Performance Schema(简称PFS)是MySQL内置的运行时诊断监控系统,它通过在MySQL内核代码中埋点的方式,采集事件数据并存储在内存中的环形缓冲区里。与慢查询日志不同,PFS是实例级的、实时可查的,开销可控,且能深入到锁、IO、等待事件、内存分配等多个维度。

1.1 Instruments 与 Consumers 模型

PFS 的采集架构由两层模型构成:

  • Instruments(事件源):MySQL源码中的埋点,每个instrument对应一类内部事件。例如
    1
    wait/io/file/innodb/innodb_data_file

    监控InnoDB数据文件的IO操作,

    1
    statement/sql/select

    监控SELECT语句执行。启用instrument意味着开始采集该类事件。

  • Consumers(事件消费者):采集到的事件流向哪里。从
    1
    events_waits_current

    (当前事件)到

    1
    events_waits_history

    (历史)再到

    1
    events_waits_history_long

    (长历史),每一级都是一个消费者,需要单独开启。

一个事件从产生到最终可查,必须同时满足:对应的instrument已启用,且事件流转路径上的所有consumer都已开启。这是理解PFS”为什么查不到数据”的关键。

1.2 启用与配置 Performance Schema

MySQL 8.0 默认启用PFS,但默认只开启部分instrument和consumer。通过以下命令查看当前配置状态:


1
2
3
4
5
6
7
8
9
10
11
12
13
-- 查看Performance Schema是否启用
SELECT @@performance_schema;

-- 查看已启用的consumer(事件消费者)
SELECT NAME, ENABLED, TIMING
FROM performance_schema.setup_consumers
WHERE ENABLED = 'YES';

-- 查看wait类instrument的启用情况
SELECT NAME, ENABLED, TIMED
FROM performance_schema.setup_instruments
WHERE NAME LIKE 'wait/%'
LIMIT 20;

如果需要临时开启全部instrument进行排查,可以执行以下操作。但要注意,全量开启会增加约5%-10%的性能开销,排查完后应当恢复:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- 临时开启所有instrument(排查时使用)
UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES';

-- 开启所有consumer
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES';

-- 排查完成后恢复默认(仅保留核心)
UPDATE performance_schema.setup_instruments
SET ENABLED = 'NO', TIMED = 'NO'
WHERE NAME NOT LIKE 'statement/%'
  AND NAME NOT LIKE 'wait/io/file/%'
  AND NAME NOT LIKE 'wait/lock/%';

UPDATE performance_schema.setup_consumers
SET ENABLED = 'NO'
WHERE NAME LIKE '%history_long%';

对于持久化配置,建议在

1
my.cnf

中设定合理的基线:


1
2
3
4
5
6
7
8
9
[mysqld]
performance_schema = ON
# 控制历史表行数
performance_schema_events_waits_history_size = 2000
performance_schema_events_waits_history_long_size = 10000
# 控制statements digest表大小
performance_schema_digests_size = 10000
# 控制session连接表行数
performance_schema_max_connections = 2000

二、慢查询日志配置与深入分析

慢查询日志是最经典的MySQL性能诊断工具。MySQL 8.0 对其做了多项增强,包括更精确的时间统计和扩展的日志输出格式。

2.1 慢查询日志核心参数

参数 说明 推荐值
slow_query_log 是否启用慢查询日志 ON
long_query_time 超过此时间(秒)记录为慢查询 0.1 – 1.0
log_queries_not_using_indexes 记录未使用索引的查询 ON
log_slow_admin_statements 记录慢的DDL语句 ON
min_examined_row_limit 检查行数低于此值不记录 0(记录所有)
slow_query_log_file 日志文件路径 /var/log/mysql/slow.log

1
my.cnf

中的推荐配置:


1
2
3
4
5
6
7
[mysqld]
slow_query_log = ON
long_query_time = 0.5
log_queries_not_using_indexes = ON
log_slow_admin_statements = ON
log_slow_slave_statements = ON
slow_query_log_file = /var/log/mysql/slow.log

2.2 使用 mysqldumpslow 分析慢查询

mysqldumpslow 是MySQL自带的慢日志分析工具,它将相似的SQL模板化后聚合统计,帮助快速发现Top慢查询:


1
2
3
4
5
6
7
8
9
10
11
# 按平均查询时间排序,显示Top 10
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# 按总返回行数排序(发现扫表严重的查询)
mysqldumpslow -s r -t 10 /var/log/mysql/slow.log

# 按总执行次数排序(高频调用)
mysqldumpslow -s c -t 20 /var/log/mysql/slow.log

# 按累计时间排序,显示完整SQL
mysqldumpslow -s at -t 10 -v /var/log/mysql/slow.log

输出中

1
Count

表示执行次数,

1
Time

后第一列为总耗时,第二列为平均耗时,

1
Rows

列为扫描行数。形如

1
S

1
N

的标记表示字符串和数字已被参数化替换。

2.3 使用 pt-query-digest 进行深度分析

Percona Toolkit 中的 pt-query-digest 提供了比 mysqldumpslow 更强大的分析能力,能生成详细的性能报告:


1
2
3
4
5
6
7
8
9
10
11
# 基础分析
pt-query-digest /var/log/mysql/slow.log

# 只分析某段时间内的慢查询
pt-query-digest --since '2025-09-01 09:00:00' --until '2025-09-01 12:00:00' /var/log/mysql/slow.log

# 过滤特定数据库的慢查询
pt-query-digest --filter '$event->{db} eq "order_db"' /var/log/mysql/slow.log

# 分析TCP抓包的实时流量(需root权限)
tcpdump -i eth0 port 3306 -l -w - | pt-query-digest --type tcpdump -

pt-query-digest 的输出报告分为三个部分:总体统计、查询分组排名、以及每个查询的详细profile。其中Response时间百分比、Rows examined与Rows sent的比值是判断SQL效率的核心指标——一个扫描百万行只返回10行的查询必然存在索引缺陷。

三、利用 Performance Schema 定位慢查询

相比慢查询日志的事后分析,Performance Schema 提供了实时在线的查询能力,无需解析日志文件,也不受日志轮转影响。

3.1 events_statements_summary_by_digest — 核心查询性能表

这张表按SQL指纹(digest)聚合所有执行过的语句统计,是排查慢SQL的第一站:


1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
    DIGEST_TEXT,
    COUNT_STAR AS exec_count,
    ROUND(AVG_TIMER_WAIT/1000000000, 2) AS avg_ms,
    ROUND(SUM_TIMER_WAIT/1000000000, 2) AS total_ms,
    SUM_ROWS_EXAMINED AS rows_examined,
    SUM_ROWS_SENT AS rows_sent,
    FIRST_SEEN,
    LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT IS NOT NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 15;

关键指标解读:

  • 1
    COUNT_STAR

    :该SQL模板的执行总次数

  • 1
    SUM_TIMER_WAIT

    :累计执行时间,排序此列找耗时最长的SQL

  • 1
    AVG_TIMER_WAIT

    :平均每次执行时间

  • 1
    SUM_ROWS_EXAMINED

    vs

    1
    SUM_ROWS_SENT

    :扫描行数与返回行数,比值过大说明索引效率低

  • 1
    FIRST_SEEN

    /

    1
    LAST_SEEN

    :首次和最后一次执行时间

注意:PFS的时间字段单位是皮秒(picoseconds),除以

1
1000000000

转为毫秒。这是初学者最容易踩的坑。

3.2 发现全表扫描型查询


1
2
3
4
5
6
7
8
9
10
11
12
SELECT
    DIGEST_TEXT,
    COUNT_STAR,
    SUM_ROWS_EXAMINED,
    ROUND(SUM_ROWS_EXAMINED / COUNT_STAR, 0) AS avg_rows_examined,
    ROUND(AVG_TIMER_WAIT/1000000000, 2) AS avg_ms
FROM performance_schema.events_statements_summary_by_digest
WHERE SUM_ROWS_EXAMINED > 0
  AND DIGEST_TEXT NOT LIKE '%SHOW%'
  AND DIGEST_TEXT NOT LIKE '%EXPLAIN%'
ORDER BY avg_rows_examined DESC
LIMIT 10;

1
avg_rows_examined

数值极大(如数万以上)但

1
SUM_ROWS_SENT

很小时,该查询几乎肯定存在全表扫描或索引选择不当的问题。此时应结合

1
EXPLAIN

分析执行计划。

四、sys Schema — 快捷诊断视图大全

sys schema 是MySQL 8.0自带的辅助视图库,它在PFS数据之上封装了大量开箱即用的查询视图,极大降低了PFS的使用门槛。直接查询PFS底层表需要多表JOIN和复杂SQL,而sys视图已经替你做好了。

4.1 查看当前执行的语句


1
2
3
4
5
6
7
8
9
10
-- 当前所有活跃会话正在执行的SQL
SELECT * FROM sys.session
WHERE state IS NOT NULL
  AND state != 'Sleep'
ORDER BY time DESC;

-- 等价于查PFS底层表
SELECT * FROM sys.processlist
WHERE state != 'Sleep'
ORDER BY time DESC;

4.2 Top慢SQL排行


1
2
3
4
5
6
7
8
9
10
11
-- 按总执行时间排名的Top SQL
SELECT * FROM sys.statements_with_runtimes_in_95th_percentile
LIMIT 10;

-- 按平均扫描行数排名
SELECT * FROM sys.statements_with_full_table_scans
LIMIT 10;

-- 按错误次数排名
SELECT * FROM sys.statements_with_errors_or_warnings
LIMIT 10;

4.3 锁等待诊断


1
2
3
4
5
6
7
8
9
10
11
12
-- 当前正在等待锁的会话
SELECT * FROM sys.innodb_lock_waits;

-- 等待事件统计
SELECT
    event_name,
    count_star,
    ROUND(sum_timer_wait/1000000000, 2) AS total_wait_ms,
    ROUND(avg_timer_wait/1000000000, 2) AS avg_wait_ms
FROM sys.wait_classes_global_by_avg_latency
WHERE event_name LIKE 'wait/lock%'
ORDER BY total_wait_ms DESC;
1
sys.innodb_lock_waits

视图会直接展示谁在等谁的锁、锁了哪些行、持锁会话正在执行什么SQL。这是排查死锁和锁超时的利器。

4.4 IO热点分析


1
2
3
4
5
6
7
8
9
10
11
12
13
-- 哪些文件IO最重
SELECT * FROM sys.io_global_by_file_by_bytes
ORDER BY total DESC LIMIT 10;

-- 哪些表的IO最多
SELECT
    file_name,
    count_read, count_write,
    total_read, total_written
FROM sys.io_global_by_file_by_bytes
WHERE file_name LIKE '%.ibd%'
ORDER BY (total_read + total_written) DESC
LIMIT 10;

五、真实排障场景实战

场景一:数据库突然变慢,CPU飙高

接到告警后,第一步是快速定位是哪些SQL导致了CPU飙升:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- Step 1: 查看当前活跃的耗时最长的语句
SELECT
    thd_id,
    conn_id,
    user,
    current_statement,
    statement_latency,
    rows_examined,
    rows_sent,
    lock_latency
FROM sys.session
WHERE current_statement IS NOT NULL
ORDER BY statement_latency DESC
LIMIT 10;

-- Step 2: 查看历史Top SQL
SELECT
    DIGEST_TEXT,
    COUNT_STAR,
    ROUND(SUM_TIMER_WAIT/1000000000, 2) AS total_ms,
    ROUND(AVG_TIMER_WAIT/1000000000, 2) AS avg_ms,
    SUM_ROWS_EXAMINED
FROM performance_schema.events_statements_summary_by_digest
WHERE LAST_SEEN > DATE_SUB(NOW(), INTERVAL 30 MINUTE)
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

找到目标SQL后,用

1
EXPLAIN ANALYZE

(MySQL 8.0.18+)获取详细的执行成本:


1
2
3
4
EXPLAIN ANALYZE
SELECT * FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at > '2025-08-01' AND o.status = 'pending';

EXPLAIN ANALYZE 会输出每个执行阶段的实际行数和耗时,能立即发现全表扫描或Hash Join退化的问题。

场景二:锁等待超频,业务报死锁


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Step 1: 查看最近的InnoDB死锁记录
-- 需要先开启 innodb_status_output
SET GLOBAL innodb_status_output = ON;
SHOW ENGINE INNODB STATUS\G

-- Step 2: 通过PFS查看锁等待历史
SELECT
    object_schema,
    object_name,
    index_name,
    lock_type,
    lock_status,
    lock_duration,
    SQL_TEXT
FROM performance_schema.events_errors_summary_global_by_error
-- 更多使用 sys 视图
SELECT * FROM sys.innodb_lock_waits;

-- Step 3: 分析持有锁的语句是否缺少合适的索引
-- 常见原因:UPDATE/DELETE 未走索引导致锁升级为表锁

在InnoDB行锁场景中,最常见的问题是:

1
UPDATE

1
DELETE

语句的

1
WHERE

条件没有命中索引,导致InnoDB不得不对所有扫描到的行加锁,退化为接近表锁的行为。这会极大增加锁冲突的概率。解决方案是为对应的过滤列添加索引。

场景三:内存使用异常增长


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 查看各模块内存分配
SELECT
    event_name,
    current_alloc,
    high_alloc,
    total_alloc
FROM sys.memory_global_by_current_bytes
ORDER BY current_alloc DESC
LIMIT 15;

-- 查看每个会话的内存占用
SELECT
    thd_id,
    user,
    current_statement,
    current_memory_used
FROM sys.session
WHERE current_memory_used > 1048576  -- 超过1MB
ORDER BY current_memory_used DESC;

MySQL 8.0 的 Performance Schema 引入了内存instrument,可以精确追踪每个模块(InnoDB buffer pool、临时表、连接处理、查询缓存相关结构等)的内存分配。当发现

1
memory/innodb/buf_buf_pool

占用过大时,应当检查

1
innodb_buffer_pool_size

配置;而

1
memory/sql/TABLE

1
memory/sql/MyISAM

异常增长则可能与表缓存溢出有关。

六、搭建持续监控体系

临时的命令行排查适合应急,但生产环境需要持续的性能数据采集和告警体系。以下是几种主流方案:

6.1 Prometheus + mysqld_exporter

Prometheus生态是MySQL监控的事实标准。mysqld_exporter 通过PFS和SHOW STATUS采集指标,推送到Prometheus后由Grafana可视化:


1
2
3
4
5
6
7
8
9
10
# mysqld_exporter 配置 my.cnf
[client]
user = exporter_user
password = YourStrongPass!

# 为exporter创建最小权限用户
CREATE USER 'exporter_user'@'%' IDENTIFIED BY 'YourStrongPass!';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter_user'@'%';
-- MySQL 8.0 需要额外授权
GRANT SELECT ON performance_schema.* TO 'exporter_user'@'%';

Grafana 中常用的Prometheus告警规则示例:


1
2
3
4
5
6
7
8
9
# 慢查询速率告警
rate(mysql_global_status_slow_queries[5m]) > 0.5

# 连接数告警
mysql_global_status_threads_running / mysql_global_status_max_used_connections > 0.8

# InnoDB缓冲池命中率低于99%
1 - rate(mysql_global_status_buffer_pool_reads[5m])
  / rate(mysql_global_status_buffer_pool_read_requests[5m]) < 0.99

6.2 Percona PMM (Percona Monitoring and Management)

PMM 是Percona推出的开箱即用监控平台,内置了Query Analytics(QAN)组件,能直接基于PFS的

1
events_statements_summary_by_digest

表生成慢查询排行榜和时间趋势图。相比手动搭建Prometheus栈,PMM部署更快、默认图表更丰富:


1
2
3
4
5
6
7
8
9
# Docker方式部署PMM Server
docker run -d --name pmm-server \
  --restart always \
  -p 80:80 -p 443:443 \
  percona/pmm-server:2

# 在MySQL节点安装PMM Client并注册
pmm-admin config --server-name pmm-host --server-insecure-tls
pmm-admin add mysql --username exporter_user --password 'YourStrongPass!' mysql-prod

6.3 自建轻量巡检脚本

对于中小团队,一个定时执行的Shell脚本也能起到很好的监控作用。以下脚本每5分钟采集一次Top慢SQL并写入日志:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# slow_query_inspector.sh
LOG_FILE="/var/log/mysql/slow_inspector.log"
MYSQL_CMD="mysql -u monitor_user -pYourPass -h 127.0.0.1"

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "=== $TIMESTAMP ===" >> "$LOG_FILE"

$MYSQL_CMD -e "
SELECT
    DIGEST_TEXT,
    COUNT_STAR AS exec_count,
    ROUND(AVG_TIMER_WAIT/1000000000, 2) AS avg_ms,
    SUM_ROWS_EXAMINED AS rows_examined,
    LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT IS NOT NULL
  AND LAST_SEEN > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;
" >> "$LOG_FILE" 2>&1

echo "" >> "$LOG_FILE"

配合crontab每5分钟执行一次即可形成基线数据。对于突发性能问题,这些历史快照能帮助回溯定位。

七、PFS性能开销控制最佳实践

Performance Schema虽然设计为低开销,但不合理的配置仍可能造成显著的性能影响。以下是生产环境的关键控制原则:

  • 不要长期全量开启instrument:默认配置已覆盖statement和核心wait事件。全量开启应在排障窗口内临时使用,结束后恢复。
  • 合理设置历史表大小
    1
    events_statements_history_long

    默认10000行,高并发下会快速轮转丢失数据。如需更长时间窗口,适当增大但注意内存消耗。

  • 监控PFS自身的内存占用:通过
    1
    SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name LIKE 'memory/performance_schema/%'

    检查PFS本身消耗的内存。

  • 定期清理digest表
    1
    TRUNCATE TABLE performance_schema.events_statements_summary_by_digest

    可以重置统计,用于在新时间窗口开始干净计数。

  • 对只读从库使用更激进的配置:从库不直接承接写业务,可以开启更详细的instrument用于分析。

检查PFS自身的内存上限:


1
2
3
4
5
6
7
8
SELECT
    SUBSTRING_INDEX(event_name, '/', 2) AS module,
    ROUND(SUM(current_number_of_bytes_used)/1024/1024, 2) AS current_mb,
    ROUND(SUM(high_number_of_bytes_used)/1024/1024, 2) AS peak_mb
FROM performance_schema.memory_summary_global_by_event_name
WHERE event_name LIKE 'memory/performance_schema/%'
GROUP BY module
ORDER BY current_mb DESC;

总结

MySQL 8.0 的 Performance Schema 和慢查询日志体系构成了从宏观监控到微观定位的完整诊断链路。在实际生产中,建议遵循”先宏观后微观”的排查路径:先通过sys schema的聚合视图定位Top SQL或热点等待事件,再针对具体SQL使用EXPLAIN ANALYZE和PFS的statement历史表深入分析执行细节,最后通过索引优化或SQL重写解决问题。

同时,不要忽视持续监控的价值。一次性的命令行排查能解决当下的瓶颈,但只有建立基于Prometheus或PMM的长期监控体系,才能在性能问题影响用户之前提前发现趋势性恶化。DBA的终极目标不是快速救火,而是让系统在大多数时候无需救火。

MySQL性能监控与诊断实战

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » MySQL 8.0 Performance Schema与慢查询监控深度实战:从指标采集到问题诊断全流程指南
分享到: 更多 (0)