编程 pt-query-digest 默认只报 20 条:--limit、--outliers、--since 的统计口径

2026-11-11 00:04:16

pt-query-digest 默认只报 20 条:--limit、--outliers、--since 的统计口径

1. 135 条慢日志,报告里 141 个 Rank

Percona 论坛 2024-06 有一个帖子:用户手工数慢日志 135 条,pt-query-digest 报告里数出 141 个 Rank。版主让加 --limit=100%,加后正常。

另一类问题更常见:默认执行 pt-query-digest slow.log 只输出 20 条,容易被当成"这个库就这几条慢查询"。两个现象都出在默认值上。

2. --limit 默认 95%:20 是两道截断

type: Array,default: 95%:20。三种写法:

  • 纯整数:只报最差的前 N 条查询
  • 整数加 %:报占总响应时间该百分比的最差查询
  • 百分比后跟冒号和一个整数:report the top percentage or the number specified by that integer, whichever comes first

默认值 95%:20 是第三种:先按总响应时间占比累积到 95% 截一刀,再按条数上限 20 截一刀,谁先到算谁。

被这两刀(以及 --outliers)扔掉的行不会消失,而是汇成报告最后一行 MISC:A final line whose rank is shown as MISC contains aggregate statistics on the queries that were not included in the report, due to options such as --limit and --outliers. 也就是说 MISC 不是一个叫"其它查询"的分类,而是被选项丢弃行的聚合。

值是逗号分隔数组,每个 --group-by 项对应一个值;未指定时默认 top 95%。--group-by 默认 fingerprint,另有 tables(按表聚合,多表 join 会被重复计次)和 distill(更粗的指纹,如 INSERT SELECT table1 table2)。注意 --group-by user,db 是"按 user 出一份报告、再按 db 出一份报告",不是复合分组。

3. --outliers 会把查询强行塞回报告

type: array,default: Query_time:1:10。语法 attribute:percentile:count:

  • 第一段:定义 outlier 的属性
  • 第二段:与"该属性的 95th percentile"比较的数值,不是百分数
  • 第三段可选:与 cnt 聚合比较

Queries that pass this specification are added to the report, regardless of any limits you specified in --limit. 这是 --limit 之外的一条加回路径,Rank 数因此和手工条数对不上。默认的 Query_time:1:10 是 95 分位 Query_time 至少 1 秒、且出现至少 10 次;换成 --outliers Query_time:60:5 就是 95 分位 Query_time 至少 60 秒且至少出现 5 次。

4. --since 严格,--until 不严格

--since 的取值类型:N[shmd](s/m/h/d,无后缀按秒,等价于 "since N ago")、YYYY-MM-DD [HH:MM:SS]、短式 YYMMDD [HH:MM:SS]、任意 MySQL 时间表达式。

前提是 Events are assumed to be in chronological order: older at the beginning, newer at the end. 而 --since is strict: it ignores all queries until one is found that is new enough. 从文件头一路丢弃,直到遇到第一条足够新的才开始收。如果日志时间戳不单调(多实例合并、时钟回拨、写入乱序),开头那批其实够新的事件会被一起扔掉。

--until 反过来:all queries are parsed until one has a timestamp that is equal to or greater than --until. Then all subsequent queries are ignored. 遇到第一条 >= 分界就停。一边是"碰到才开始",一边是"碰到就停"。

5. --since 用 MySQL 表达式时必须给 DSN

--since 'CURRENT_DATE - INTERVAL 7 DAY' 这类表达式要连 MySQL 求值。触发条件是:如果给 MySQL 时间表达式,且没有为 --explain/--processlist/--review 指定 DSN,则必须在命令行给 DSN。

pt-query-digest --since 'CURRENT_DATE - INTERVAL 7 DAY' slow.log h=localhost

求值方式是把表达式包进 SELECT UNIX_TIMESTAMP(),所以表达式本身在这个位置必须合法。不要写 UNIX_TIMESTAMP(),因为 UNIX_TIMESTAMP(UNIX_TIMESTAMP()) 返回 0。

6. stddev 和 median 是截尾后算的

报表列:pct / total / min / max / avg / 95% / stddev / median。文档对后两列的口径写得很直接:

The standard deviation and median are both calculated from the 95th percentile, discarding the extremely large values.

先截掉 95 分位以上的极大值,再算标准差和中位数。所以"max 比 avg 大很多"不能直接当抖动证据——max 本来就在截尾范围外。看离散程度用 95% 列和 V/M。

V/M 是 response time 的 variance-to-mean ratio(文档链到 index of dispersion)。这个值大,表示同一类查询的执行时间随参数剧烈波动,而不是整体慢。

--ignore-attributes 的默认值决定了报告里没有哪些字段:arg, cmd, insert_id, ip, port, Thread_id, timestamp, exptime, flags, key, res, val, server_id, offset, end_log_pos, Xid。

补充一个输入侧的数据失真:Aurora 慢日志会把很快的查询记成巨大的 Query_time / Lock_time,直接用会污染 max 和 95%。Percona 博客给的绕法是 --attribute-value-limit=4294967296(type: int,默认 0,即不设限),把这批记录忽略掉。

7. --type 不同,缺的属性也不同

默认 slowlog。几种输入格式的差异直接影响口径:

  • binlog:需要先转文本:mysqlbinlog mysql-bin.000441 > mysql-bin.000441.txt; pt-query-digest --type binlog mysql-bin.000441.txt
  • genlog:general log 缺大量属性,尤其是 Query_time。默认 --order-by 因此变成 Query_time:cnt——所有 Query_time 都是 0,只有 cnt 有意义。--order-by 语法是 attribute:aggregate,聚合只有 sum/min/max/cnt,默认 Query_time:sum;指定了事件里不存在的属性,会退回默认值并在报告开头打印提示。
  • tcpdump:pt-query-digest 不抓包,只解析 tcpdump 输出,且解析器要求输入由 -x -n -q -tttt 生成:tcpdump -s 65535 -x -nn -q -tttt -i any -c 1000 port 3306 > mysql.tcp.txt; pt-query-digest --type tcpdump mysql.tcp.txt。3306 端口自动识别,非 3306 要 --watch-server。丢包会让工具看不到请求的响应,把后续响应的耗时错配给这条查询。SSL 加密流量无法解析,也不支持 Unix socket 抓包。
  • rawlog:每行一条 SQL 的纯文本,没有指标,很多功能不适用。

--output 默认 report,可选 report / slowlog / json / json-anon / secure-slowlog。--no-report 关掉报告以跳过昂贵的聚合,配合 --review、--history 时建议加。把 general log 转成 slow log 再喂给 pt-index-usage,靠的就是 --output slowlog。

8. --filter 的两个坑

--filter 的定义是 Discard events for which this Perl code doesn't return true. 两个具体问题:

  1. 属性在事件里可能没有定义。直接写 $event->{db} =~ ...,一旦某条事件缺这个字段,工具会崩,报 use of uninitialized value。要写成:
pt-query-digest --filter '($event->{db} || "") =~ m/^orders$/' slow.log
  1. Full_scan / Full_join 这类属性只有 Percona Server 的 slow log 扩展才有(log_slow_verbosity、log_slow_filter 的 full_scan / full_join / tmp_table / tmp_table_on_disk / filesort / filesort_on_disk,以及 log_slow_rate_type / log_slow_rate_limit)。在原生 MySQL 上按这些属性过滤,结果恒为空——不是没有全表扫描,是字段不存在。

9. 先看全量,再收窄

# 全量口径:吃掉 95% 截断
pt-query-digest --limit=100% slow.log > full.txt

# 百分比与条数同时给(先到者算)
pt-query-digest --limit=100%:50 slow.log

# 每类查询取前 2 条样本,不做聚合
pt-query-digest --sample 2 --no-report --output slowlog slow.log > sample.log

# general log 转 slow log,再喂给 pt-index-usage
pt-query-digest --type genlog --output slowlog general.log > general-slow.log

# 跟踪:review 标记已看过,history 存趋势
pt-query-digest --review --history slow.log

配套的默认值细节:

  • --sample N 按 --group-by 的第一个值过滤,只留每类查询的前 N 次。
  • --review 保存所有 fingerprint,下次运行不打印已标记 reviewed_by 的查询,--report-all 可看全部。被 review 掉的空缺会让段落序号跳号,且 --limit 仍然生效——如果前 10 都被 review 过,就什么也不输出。它依赖 --group-by fingerprint(默认)。

10. 项目信息

  • 官方文档:https://docs.percona.com/percona-toolkit/pt-query-digest.html
  • 仓库:https://github.com/percona/percona-toolkit
  • 版本:3.7.2
  • 安装包名:percona-toolkit(3.7.1 起在 deb13、Amazon Linux 2023 上构建)

还有一个没验证的点:--outliers 强行加回报告的查询,如果同时也落在 MISC 的聚合范围内,total 计次会不会重复算两遍。

推荐文章

程序员茄子在线接单