Performance Schema:尚未开发的金矿
performance_schema 在 MariaDB / MySQL 中默认启用已有多年。然而,大多数 DBA 并不会日常使用它。原因很简单:原始数据难以阅读。数十张表、数百万行、累计计数器——没有聚合工具的话,这些只是噪音。
PmaControl 将噪音转化为信号。它通过 Aspirateur 收集 performance_schema 数据,通过 Listener(Digest::integrate)进行聚合,并以可操作的仪表盘呈现。本文将介绍从数据源到仪表盘的完整管道。
验证 performance_schema 是否已启用
MariaDB
SHOW GLOBAL VARIABLES LIKE 'performance_schema';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| performance_schema | ON |
+--------------------+-------+
如果显示 OFF,请在配置文件中添加:
[mysqld]
performance_schema = ON
需要重启服务——此变量不支持动态修改。
MySQL
MySQL 上同样如此。该变量是只读的,需要重启:
[mysqld]
performance_schema = ON
性能影响
经典问题:"performance_schema 会拖慢我的服务器吗?"2026 年的答案是不会,影响无法察觉。在典型工作负载下,开销在 1-3% 范围内。可见性带来的收益远远超过这一成本。
唯一的例外:极高工作负载的服务器(每秒超过 100,000 次查询),每一个百分点都很重要。在这种情况下,应禁用不必要的探针,而不是完全关闭 performance_schema。
数据源:events_statements_summary_by_digest
PmaControl 利用的关键表是:
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10\G
该表包含按指纹(规范化签名)汇总的每个已执行查询的摘要。以下是最有用的列:
| 列 | 说明 |
|---|---|
DIGEST |
指纹的唯一哈希 |
DIGEST_TEXT |
规范化的查询文本(参数替换为 ?) |
COUNT_STAR |
总执行次数 |
SUM_TIMER_WAIT |
总执行时间(以皮秒为单位) |
AVG_TIMER_WAIT |
每次执行的平均时间 |
SUM_ROWS_EXAMINED |
检查的总行数 |
SUM_ROWS_SENT |
返回的总行数 |
FIRST_SEEN |
首次执行时间 |
LAST_SEEN |
最后执行时间 |
指纹是核心:它将 SELECT * FROM users WHERE id = 42 和 SELECT * FROM users WHERE id = 1337 规范化为单一指纹 SELECT * FROM users WHERE id = ?。这样就可以不受参数值影响地聚合统计信息。
PmaControl 管道
第一步:Aspirateur 采集
Aspirateur 定期在每个受监控的服务器上执行以下查询:
SELECT
DIGEST,
DIGEST_TEXT,
COUNT_STAR,
SUM_TIMER_WAIT,
AVG_TIMER_WAIT,
SUM_ROWS_EXAMINED,
SUM_ROWS_SENT,
SUM_NO_INDEX_USED,
SUM_NO_GOOD_INDEX_USED,
FIRST_SEEN,
LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST IS NOT NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 500;
LIMIT 500 是有意为之的:PmaControl 专注于按累计时间计算的前 500 个最耗时查询。快速且不频繁的查询对优化没有意义。
结果与时间戳一起存储在 ts_value_general_json 中。
第二步:Listener 处理
当 Listener 检测到新的摘要数据时,会触发 Digest::integrate()。该函数执行以下操作:
-
计算增量:由于
performance_schema提供累计计数器(自上次TRUNCATE或重启以来),Digest::integrate 计算两次采集之间的差值以获取期间指标。 -
时间单位转换:将皮秒转换为毫秒以便显示。
-
计算百分位数:从分布中估算 P95(第 95 百分位)执行时间。
-
存储到 ts_mysql_digest_stat:专用的摘要统计表,按天分区并使用 RocksDB 引擎进行压缩。
Aspirateur
-> SELECT FROM performance_schema(每分钟)
-> INSERT INTO ts_value_general_json
Listener
-> 检测新数据(ts_max_date 变化)
-> Digest::integrate()
-> 增量计算(当前 - 前一次)
-> 转换为毫秒
-> 估算 P95
-> INSERT INTO ts_mysql_digest_stat
ts_mysql_digest_stat 表
这是摘要统计数据的长期存储:
CREATE TABLE ts_mysql_digest_stat (
id BIGINT UNSIGNED AUTO_INCREMENT,
server_id INT UNSIGNED,
digest VARCHAR(64),
digest_text TEXT,
period_start DATETIME,
period_end DATETIME,
exec_count BIGINT UNSIGNED,
total_time_ms DECIMAL(20,3),
avg_time_ms DECIMAL(15,3),
p95_time_ms DECIMAL(15,3),
rows_examined BIGINT UNSIGNED,
rows_sent BIGINT UNSIGNED,
no_index_used BIGINT UNSIGNED,
PRIMARY KEY (id),
KEY idx_server_digest (server_id, digest, period_start)
) ENGINE=ROCKSDB
PARTITION BY RANGE (TO_DAYS(period_start)) (
PARTITION p20260413 VALUES LESS THAN (TO_DAYS('2026-04-14')),
PARTITION p20260414 VALUES LESS THAN (TO_DAYS('2026-04-15')),
...
);
按天分区实现了:
- 快速清理:
ALTER TABLE ts_mysql_digest_stat DROP PARTITION p20260401; - 快速的日期范围查询
- 精细的数据保留控制
仪表盘
最耗时查询视图
主仪表盘显示按累计时间排序的最耗时查询:
排名 指纹 执行/小时 平均(ms) P95(ms) 检查行数
----- ---------------------------------------- -------- -------- -------- ---------
1 SELECT * FROM orders WHERE customer_id 12,430 45.2 234.5 1,245,000
= ? AND status = ?
2 UPDATE inventory SET stock = stock - ? 8,200 12.3 89.1 820,000
WHERE product_id = ?
3 SELECT o.*, c.name FROM orders o JOIN 3,150 78.9 445.2 3,150,000
customers c ON o.customer_id = c.id
4 INSERT INTO audit_log (...) 45,600 1.2 5.3 0
5 SELECT COUNT(*) FROM sessions WHERE 980 234.5 890.1 98,000,000
last_active < ?
每一行都可以点击以查看详细信息。
指纹详情视图
点击某个指纹后,PmaControl 会显示:
- 完整文本:规范化查询的全文
- 历史记录:过去 30 天平均执行时间和 P95 的变化趋势
- 比率:rows_examined / rows_sent——高比率(>100:1)表示全表扫描或缺失索引
- no_index_used 标志:多少次执行未使用索引
识别缺失的索引
rows_examined / rows_sent 比率是最强大的指标。来看一个例子:
指纹:SELECT * FROM orders WHERE customer_id = ?
检查行数:1,245,000(总计)
返回行数:12,430(总计)
比率:100:1
这个 100:1 的比率意味着 MariaDB / MySQL 检查了 100 行才返回 1 行。这是全表扫描或索引无效的典型迹象。
操作:检查 customer_id 上是否有索引:
SHOW INDEX FROM orders WHERE Column_name = 'customer_id';
如果索引不存在:
ALTER TABLE orders ADD INDEX idx_customer_id (customer_id);
SUM_NO_INDEX_USED 标志
PmaControl 会用红色标记 SUM_NO_INDEX_USED 值较高的查询。当 MariaDB / MySQL 执行全表扫描时会设置此标志——这通常是性能问题的头号原因。
从 PmaControl 执行 EXPLAIN
对于被识别为有问题的查询,PmaControl 可以直接执行 EXPLAIN:
EXPLAIN SELECT * FROM orders WHERE customer_id = 42 AND status = 'pending';
结果以颜色编码显示:
- 绿色:
type = ref或type = eq_ref——使用了索引,良好 - 橙色:
type = range——范围扫描,可接受 - 红色:
type = ALL——全表扫描,需要修复
与 Marina+ Agent 集成
Marina+ 是 PmaControl 的自动优化代理。它分析摘要数据并提出建议:
- 缺失索引:检测 rows_examined/rows_sent 比率高的查询,并建议创建索引
- 查询重写:识别低效模式(SELECT *、关联子查询、在非索引列上 ORDER BY)
- 配置调优:根据查询模式调整服务器参数(sort_buffer_size、join_buffer_size 等)
Marina+ 不会自动修改任何内容——它生成的建议需要 DBA 验证和应用。
与 PMM(Percona 监控管理)的比较
PMM 和 PmaControl 利用相同的数据源(performance_schema),但采用不同的方法:
| 方面 | PmaControl | PMM |
|---|---|---|
| 数据源 | performance_schema | performance_schema + slowlog |
| 代理 | Aspirateur(SSH + MySQL) | mysqld_exporter + QAN |
| 存储 | ts_mysql_digest_stat(RocksDB) | ClickHouse(QAN) |
| 指纹提取 | 服务器端(MariaDB / MySQL 原生) | 客户端(Percona agent) |
| P95 | 从分布估算 | 从慢查询日志计算 |
| 历史记录 | 按天分区,可配置保留策略 | ClickHouse,可配置保留策略 |
| 操作 | 集成 EXPLAIN,Marina+ 建议 | Query Analytics + PMM UI |
主要区别:PMM 结合了 performance_schema 和慢查询日志以获得更精确的百分位数。PmaControl 仅依赖 performance_schema,更轻量但粒度较粗。
PmaControl 的优势:与生态系统其他部分的集成(复制、拓扑、告警、操作)。PMM 在纯查询分析方面更出色,PmaControl 在全局基础设施视图方面更出色。
实战案例:发现并修复慢查询
场景:PmaControl 仪表盘显示某个查询消耗了服务器总时间的 40%。
第一步:识别
在最耗时查询仪表盘中:
#1 SELECT u.*, p.* FROM users u
JOIN purchases p ON u.id = p.user_id
WHERE p.created_at > ? AND u.country = ?
执行/小时: 5,200 平均: 234ms P95: 1,200ms 检查行数: 45M
第二步:分析
rows_examined / rows_sent 比率非常糟糕:每小时检查 4500 万行却仅返回约 5,200 个结果。
PmaControl 的 EXPLAIN 输出:
+----+------+----------+------+------+------+----------+
| id | type | table | key | rows | filt | Extra |
+----+------+----------+------+------+------+----------+
| 1 | ALL | users | NULL | 1.2M | 10% | where |
| 1 | ref | purchases| idx1 | 15 | 33% | where |
+----+------+----------+------+------+------+----------+
问题所在:users 表进行了全表扫描(type = ALL)。country 列上没有索引。
第三步:修复
ALTER TABLE users ADD INDEX idx_country (country);
第四步:验证
添加索引后,PmaControl 仪表盘在一小时内显示出改善:
#1 SELECT u.*, p.* FROM users u
JOIN purchases p ON u.id = p.user_id
WHERE p.created_at > ? AND u.country = ?
执行/小时: 5,200 平均: 12ms P95: 45ms 检查行数: 78K
平均时间从 234ms 降至 12ms(19 倍),检查行数从 4500 万降至 78K(577 倍)。
最佳实践
1. 不要手动 TRUNCATE performance_schema
PmaControl 计算两次采集之间的增量。如果您运行 TRUNCATE TABLE performance_schema.events_statements_summary_by_digest,计数器会从零重新开始,第一次增量计算将不正确。请让 PmaControl 来处理。
2. 必要时增加 performance_schema_digests_size
默认情况下,MariaDB / MySQL 存储前 N 个指纹。如果您的应用程序的不同查询数量超过限制,最不频繁的查询将被淘汰:
[mysqld]
performance_schema_digests_size = 10000 ; 默认约 5000
3. 与慢查询日志关联分析
PmaControl 通过 performance_schema 提供"什么"(哪些查询慢)。慢查询日志提供"何时"(在哪个确切时刻)。两者是互补的。
4. 关注 rows_examined / rows_sent 比率
这是最具可操作性的指标。比率 > 100:1 几乎总是意味着缺失索引。比率 > 1000:1 则是紧急问题。
5. 使用 P95,而非平均值
平均值会掩盖异常值。一个平均 10ms 但 P95 为 500ms 的查询存在间歇性问题(锁竞争、冷缓存、不稳定的执行计划)。P95 能揭示这些问题。
结论
Performance_schema 是 MariaDB / MySQL 查询优化的最佳数据源。PmaControl 通过 Aspirateur -> Digest::integrate -> ts_mysql_digest_stat -> 仪表盘的管道自动化了数据的收集、聚合和呈现。
结果:对最耗时的查询、缺失的索引和随时间变化的性能趋势拥有持续的可见性。结合 Marina+ 提供的自动化建议,这是一个完整的性能优化工作流——从发现到修复。
评论 (0)
暂无评论。
发表评论