最稳做法是用 pt-query-digest 原生跨文件聚合,配合 --filter 标注 server_id 并 --group-by fingerprint,server_id;禁用 cat 拼接,避免时间戳混乱;performance_schema 不可替代 slow log 回溯分析;grafana 对比需通过 prometheus+mysqld_exporter 预聚合指标实现。
用 pt-query-digest 聚合多实例慢日志最稳
直接上结论:别自己写脚本拼接日志,pt-query-digest 原生支持跨文件、跨实例聚合,还能自动去重和归一化 sql。它不依赖数据库连接,纯日志分析,出错率低、结果可信。
常见错误是把各台服务器的 slow.log 直接 cat 到一起再喂给 pt-query-digest——这会导致时间戳混乱、客户端 IP 混淆、无法按实例分组。正确做法是用 --group-by + --filter 显式标注来源:
- 每台服务器导出时加标识:
pt-query-digest --filter '$event->{server_id} = "prod-app-01"' /var/log/mysql/slow.log > prod-app-01.digest - 或统一读取并打标:
pt-query-digest --filter '$event->{server_id} = "prod-db-02"' /data/logs/db2/slow.log --filter '$event->{server_id} = "prod-db-03"' /data/logs/db3/slow.log -
--group-by fingerprint,server_id是关键,否则所有实例的相同 SQL 会合并成一条,失去实例维度
MySQL 8.0+ 的 performance_schema 没法直接替代 slow log 聚合
有人想绕过日志文件,直接查各实例的 performance_schema.events_statements_summary_by_digest——这只能看“当前累积”,不能回溯历史、无法跨时段比对,而且默认只保留 10000 条摘要,高频小查询容易被刷掉。
更现实的用法是把它当辅助:用 SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 20 快速定位当前最耗时的指纹,但别指望靠它做周粒度趋势分析。
- slow log 是磁盘持久化的原始记录,
performance_schema是内存中滚动摘要,二者用途不同 -
performance_schema不记录Query_time和Lock_time的原始值,只有聚合后的SUM_TIMER_WAIT,没法算 P95 延迟 - 开启
performance_schema对高并发实例有 3%–5% 性能损耗,而 slow log 开销可控(尤其配合long_query_time = 1)
Grafana 仪表盘里怎么让不同实例的慢查询指标可对比
核心是别在 Grafana 里硬写多个数据源,先在后端统一建模。推荐用 Prometheus + mysqld_exporter,但注意:它默认不暴露慢查询统计,得手动开启 --collect.global_status --collect.info_schema.processlist,再配合自定义 SQL 抓取慢查询计数:
- 在每台 MySQL 上建视图:
CREATE VIEW slow_count_1h AS SELECT COUNT(*) c FROM mysql.slow_log WHERE start_time > NOW() - INTERVAL 1 HOUR - 配置
mysqld_exporter的custom_queries.yml,执行该视图并暴露为mysql_slow_query_count_1h指标 - Grafana 中用
sum by (instance) (mysql_slow_query_count_1h)就能横向对比,且天然带instance标签
如果强行用 Loki 收集原始 slow log 行,查询会极慢——一条慢日志平均 200 字节,一天 100 万条就是 200MB,Loki 的 regex 提取 + label 打标开销远高于预聚合。
为什么用 rsync + cron 同步日志到中心节点总出问题
不是 rsync 不行,是没处理好三个细节:文件锁、轮转竞争、时区偏移。MySQL 写 slow log 是追加模式,但 logrotate 一触发,旧文件可能还在被写入,rsync 就会复制到一半的脏内容。
- 同步前加检查:
lsof -t /var/log/mysql/slow.log >/dev/null || rsync ...,避免拷贝正在写的文件 - logrotate 配置必须加
copytruncate,而不是create,否则 rsync 可能遇到文件消失 - 所有服务器统一设为 UTC 时区,否则
pt-query-digest --since "2024-06-01 00:00:00"在不同实例上含义不同
真正省心的做法是改用 mysqlbinlog 风格的流式采集:用 tail -F + awk 实时截取新日志行,打上 host=xxx 标签后发到 Kafka 或本地文件队列,再由消费者写入中心存储——但这需要额外运维成本,中小团队优先选 pt-query-digest 定期批处理。










