用 mysqldumpslow -s c -t 20 日志路径按执行次数排序取前20,它自动归一化参数识别模板级高频SQL;PostgreSQL则通过 pg_stat_statements 扩展查 calls 字段排序。

MySQL 慢查询日志里怎么筛出执行次数最多的 SQL?
慢查询日志(slow_query_log)默认只记录耗时超阈值的语句,不自带频次统计。想快速定位“高频 SQL”,得靠后处理:先确保日志格式支持解析,再用命令行工具聚合。
- 确保
log_output设为FILE或TABLE(推荐FILE,便于用mysqldumpslow或awk处理) - 开启
long_query_time = 0可捕获所有查询(仅限调试,线上慎用) - 若用
log_output = TABLE,数据存在mysql.slow_log表,但默认不索引sql_text,GROUP BY效率低
常用做法是导出日志文件后用 mysqldumpslow:
mysqldumpslow -s c -t 20 /var/lib/mysql/slow.log
其中 -s c 表示按执行次数排序,-t 20 取前 20 条。注意:它会自动归一化参数(如把 WHERE id = 123 和 WHERE id = 456 视为同一条),适合识别模板级高频语句。
PostgreSQL 的 pg_stat_statements 怎么查调用最频繁的 SQL?
PostgreSQL 不依赖文本日志,而是靠扩展 pg_stat_statements 实时统计。它天然记录 calls(执行次数)、total_time、mean_time 等字段,查高频 SQL 更直接。
- 需先在
postgresql.conf中启用:shared_preload_libraries = 'pg_stat_statements',并重启 - 执行
CREATE EXTENSION pg_stat_statements; - 查询示例:
SELECT calls, total_time, query FROM pg_stat_statements ORDER BY calls DESC LIMIT 10;
注意三点:
-
query字段是归一化后的语句(常量被$1替代),和实际日志里的原始 SQL 不完全一致 - 默认只跟踪 top-N 查询(由
pg_stat_statements.max控制,建议设为 10000+) - 数据不清零,长期运行后可能掩盖近期突增的高频语句,可定期用
pg_stat_statements_reset()清空重计
为什么用正则或 grep 直接扫慢日志容易漏掉高频 SQL?
直接对 MySQL 慢查询日志做 grep 或 awk '{print $NF}' 是常见误区,问题在于:
- 日志每条记录包含多行(
# Time、# User@Host、# Query_time、实际 SQL),简单按行切分会导致 SQL 被截断或错位 - 同一逻辑 SQL 参数不同(如
SELECT * FROM orders WHERE user_id = 1001vs...user_id = 1002)会被当作两条,无法聚合成“同一类” - 未过滤注释、空行、非查询语句(如
SET、USE),干扰统计结果
更稳妥的做法是用 pt-query-digest(Percona Toolkit):
- 自动解析日志结构,提取完整 SQL
- 支持按 fingerprint(抽象语法树哈希)归类,比简单正则可靠得多
- 可输出调用频次、平均延迟、锁等待时间等多维指标
高频 SQL 不一定等于问题 SQL,怎么判断是否真要优化?
执行次数高 ≠ 必须优化。比如一个 SELECT COUNT(*) FROM status WHERE type = 'active' 每秒跑 50 次,如果走索引且响应稳定在 0.5ms,它只是“忙”,不是“病”。
判断依据建议组合看三项:
-
calls(调用次数)是否显著高于其他语句(例如 Top 3 占总查询量 70%+) -
avg_time或mean_time是否持续高于 P95 基线(比如多数查询 - 对应表是否频繁出现在
SHOW ENGINE INNODB STATUS的SEMAPHORES或TRANSACTIONS区域(暗示锁争用)
特别注意:缓存层(如 Redis)未覆盖的“高频 + 低变更”查询,往往比“低频 + 高耗时”的更值得加缓存——因为优化收益是乘数关系,不是加法。










