用 mysqldumpslow -s c -t 20 日志路径按执行次数排序取前20,它自动归一化参数识别模板级高频SQL;PostgreSQL则通过 pg_stat_statements 扩展查 calls 字段排序。
慢查询日志(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 实时统计。它天然记录 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 不完全一致 pg_stat_statements.max 控制,建议设为 10000+) pg_stat_statements_reset() 清空重计 直接对 MySQL 慢查询日志做 grep 或 awk '{print $NF}' 是常见误区,问题在于:
# Time、# User@Host、# Query_time、实际 SQL),简单按行切分会导致 SQL 被
SELECT * FROM orders WHERE user_id = 1001 vs ...user_id = 1002)会被当作两条,无法聚合成“同一类” SET、USE),干扰统计结果 更稳妥的做法是用 pt-query-digest(Percona Toolkit):
执行次数高 ≠ 必须优化。比如一个 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)未覆盖的“高频 + 低变更”查询,往往比“低频 + 高耗时”的更值得加缓存——因为优化收益是乘数关系,不是加法。