4. SQL慢查询监控:MySQL慢查询日志配置、pt-query-digest分析、Grafana展示慢查询TOP N

慢查询是数据库性能劣化的首要信号。本章围绕“采集 → 分析 → 可视化”三条主线,讲解如何开启MySQL慢查询日志、使用pt-query-digest进行结构化解析,以及通过Grafana面板实时展示TOP N慢查询。掌握这套链路,你就能在用户感知到延迟之前定位问题SQL。

前置条件:MySQL 5.7+、Percona Toolkit已安装、Grafana 9.x+ 且已配置MySQL数据源。

4.1 MySQL慢查询日志配置

慢查询日志是MySQL原生提供的性能诊断工具,记录执行时间超过 long_query_time 阈值的SQL语句。以下为生产环境推荐配置:

参数名 推荐值 说明
slow_query_log ON 开启慢查询日志
slow_query_log_file /var/log/mysql/slow.log 日志文件路径,确保MySQL用户可写
long_query_time 1 单位秒,超过1秒即记录;分析阶段可临时设为0
log_queries_not_using_indexes ON 记录未使用索引的查询,即使执行时间很短
log_slow_admin_statements ON 记录慢管理语句(ALTER、ANALYZE等)
min_examined_row_limit 1000 扫描行数超过此值才记录,过滤小表全表扫描

动态配置示例(无需重启MySQL):

-- 全局动态开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = ON;
SET GLOBAL log_slow_admin_statements = ON;
SET GLOBAL min_examined_row_limit = 1000;

-- 确认配置生效
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
注意:生产环境不建议将 long_query_time 设为0,否则会记录所有查询,导致日志文件急剧膨胀,影响磁盘I/O。建议先设为1秒,待分析阶段再临时调低。
慢查询日志采集与轮转流程 MySQL实例 slow.log logrotate轮转 pt-query-digest 分析报告/JSON Grafana面板 建议:日志轮转周期设为每天一次,保留最近7天日志,避免磁盘写满

4.2 pt-query-digest分析慢查询

pt-query-digest是Percona Toolkit中最核心的分析工具,能将原始慢查询日志解析为结构化报告,按查询指纹(Query Fingerprint)聚合,并计算每个指纹的响应时间占比、执行次数、平均延迟等关键指标。

4.2.1 基本用法

# 分析慢查询日志,输出到终端
pt-query-digest /var/log/mysql/slow.log

# 输出到文件,便于后续处理
pt-query-digest /var/log/mysql/slow.log > /tmp/slow_report.txt

# 分析最近1小时的慢查询(配合日志轮转)
pt-query-digest --since=3600 /var/log/mysql/slow.log

# 输出JSON格式,便于Grafana或脚本消费
pt-query-digest --output=json /var/log/mysql/slow.log > /tmp/slow_report.json

4.2.2 报告解读

pt-query-digest报告分为三个主要部分:

报告段落 内容 重点关注
Overall 总执行次数、总耗时、唯一查询指纹数 总耗时是否异常增长
Profile 按响应时间占比排序的查询指纹列表 TOP N查询的响应时间占比
Query details 每个查询指纹的详细统计(执行计划、行数、延迟分布) Rows_examined与Rows_sent的比值
关键指标:响应时间占比(Pct of total time)超过20%的查询指纹应优先优化;Rows_examined / Rows_sent 比值超过1000说明索引缺失或查询逻辑需要重构。
pt-query-digest 分析流水线 原始slow.log SQL指纹提取 按指纹聚合 按总耗时排序 文本报告 / JSON 导入MySQL表 / 文件 提示:使用 --output=json 配合 jq 命令可快速提取TOP N查询指纹

4.3 Grafana展示慢查询TOP N

将pt-query-digest的分析结果导入MySQL表后,即可通过Grafana构建实时慢查询监控面板。推荐的数据模型如下:

4.3.1 数据导入表结构

CREATE TABLE slow_query_digest (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    fingerprint_md5 CHAR(32) NOT NULL,
    fingerprint TEXT NOT NULL,
    sample_query TEXT,
    db_name VARCHAR(64),
    exec_count INT NOT NULL DEFAULT 0,
    total_time_sec DECIMAL(12,4) NOT NULL DEFAULT 0,
    pct_total_time DECIMAL(5,2) NOT NULL DEFAULT 0,
    avg_time_sec DECIMAL(10,4) NOT NULL DEFAULT 0,
    rows_examined_avg INT NOT NULL DEFAULT 0,
    rows_sent_avg INT NOT NULL DEFAULT 0,
    first_seen DATETIME,
    last_seen DATETIME,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_last_seen (last_seen),
    INDEX idx_total_time (total_time_sec DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

4.3.2 导入脚本示例

#!/bin/bash
# 每小时执行一次,将pt-query-digest结果导入MySQL
REPORT_FILE="/tmp/slow_report.json"
MYSQL_HOST="127.0.0.1"
MYSQL_USER="grafana"
MYSQL_PASS="your_password"
DB_NAME="monitor"

pt-query-digest --output=json /var/log/mysql/slow.log > $REPORT_FILE

# 使用Python解析JSON并写入MySQL(简化示例)
python3 << EOF
import json, pymysql

with open("$REPORT_FILE") as f:
    data = json.load(f)

conn = pymysql.connect(host="$MYSQL_HOST", user="$MYSQL_USER",
                       password="$MYSQL_PASS", database="$DB_NAME")
cur = conn.cursor()

for query in data["queries"]:
    md5 = query["fingerprint_md5"]
    # 使用INSERT ... ON DUPLICATE KEY UPDATE 更新统计
    sql = """INSERT INTO slow_query_digest
             (fingerprint_md5, fingerprint, sample_query, db_name,
              exec_count, total_time_sec, pct_total_time,
              avg_time_sec, rows_examined_avg, rows_sent_avg,
              first_seen, last_seen)
             VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
             ON DUPLICATE KEY UPDATE
             exec_count=VALUES(exec_count),
             total_time_sec=VALUES(total_time_sec),
             last_seen=VALUES(last_seen)"""
    cur.execute(sql, (md5, query["fingerprint"], query["sample"],
                      query.get("db",""), query["exec_count"],
                      query["total_time"], query["pct_total_time"],
                      query["avg_time"], query["rows_examined_avg"],
                      query["rows_sent_avg"], query["first_seen"],
                      query["last_seen"]))
conn.commit()
cur.close()
conn.close()
EOF

4.3.3 Grafana面板配置

在Grafana中创建Dashboard,添加以下面板:

面板类型 查询SQL 展示目标
Table SELECT fingerprint_md5 AS 指纹ID, LEFT(fingerprint,80) AS 查询摘要, exec_count AS 执行次数, total_time_sec AS 总耗时(秒), ROUND(avg_time_sec,4) AS 平均耗时, ROUND(rows_examined_avg/rows_sent_avg,0) AS 扫描/返回比 FROM slow_query_digest ORDER BY total_time_sec DESC LIMIT 20 展示TOP 20慢查询明细
Bar Gauge SELECT fingerprint_md5 AS metric, pct_total_time AS value FROM slow_query_digest ORDER BY total_time_sec DESC LIMIT 10 TOP 10慢查询响应时间占比
Stat SELECT SUM(exec_count) AS 总执行次数, SUM(total_time_sec) AS 总耗时 FROM slow_query_digest WHERE last_seen > NOW() - INTERVAL 1 HOUR 过去1小时慢查询总量
调优建议:在Table面板中启用“列排序”和“搜索”功能,方便DBA快速过滤特定数据库或关键词;Bar Gauge面板建议设置阈值:绿色 < 5%,黄色 5%~20%,红色 > 20%。
Grafana慢查询TOP N面板架构 MySQL数据源 slow_query_digest表 SQL查询(ORDER BY + LIMIT) Table面板 Bar Gauge Stat面板 最佳实践:Table面板显示TOP 20,Bar Gauge显示TOP 10,Stat面板显示汇总统计

4.4 报警规则配置

基于慢查询数据,在Grafana Alerting中配置以下报警规则:

  • 慢查询总量突增:过去5分钟慢查询执行次数 > 基线值的2倍,触发Warning。
  • 单条查询超时:任意查询指纹的平均耗时 > 5秒,触发Critical。
  • 全表扫描风险:rows_examined_avg / rows_sent_avg > 5000 且 exec_count > 100,触发Warning。
避坑指南:不要对慢查询总量设置固定阈值,应使用Grafana的“动态基线”或“同比/环比”功能,避免因业务流量波动导致误报。同时,pt-query-digest分析本身会消耗CPU资源,建议在从库或专用分析实例上执行。

通过本章配置,你已经构建了一条完整的慢查询监控链路:从MySQL日志采集,到pt-query-digest结构化分析,再到Grafana实时展示与报警。下一环节将进入索引优化与查询重写实战。