pt-table-usage
分析查询如何使用表——不仅指出读写了哪些表,还指出表之间的数据流向。
语法
pt-table-usage [OPTIONS] [FILES]从日志读取查询并分析其表使用方式;未指定 FILE 时读标准输入,为每个查询打印一份报告。
用法示例
本工具主要读取慢查询日志(或 --query 给的单条查询)来分析表使用关系,多数场景不需要连接 MySQL;只有用 --explain-extended 消解列/表名歧义时才需提供 DSN。命令里的日志路径、查询串、库名替换成你自己的即可。
场景:分析慢日志的表依赖
拿到一份慢日志,先整体看每条查询都碰了哪些表、数据怎么在表之间流动:
pt-table-usage /var/log/mysql/slow.log场景:直接分析一条查询
不想先落慢日志,临时验证某条多表 UPDATE/INSERT..SELECT 的数据流向,直接用 --query:
pt-table-usage --query "UPDATE t1 JOIN t2 USING(id) SET t1.c=t2.c WHERE t1.id > 10"场景:多表查询列名歧义时消解
查询里列没加表前缀、工具无法确定列属于哪张表时,给它一台服务器执行 EXPLAIN EXTENDED 来解析(DSN 不写明文密码):
pt-table-usage /var/log/mysql/slow.log --explain-extended h=localhost场景:用建表定义消解歧义(免连库)
不方便连库时,把 mysqldump --no-data 的库表结构存成文件,让工具据此限定列归属:
pt-table-usage /var/log/mysql/slow.log --create-table-definitions /tmp/schema.sql场景:日志没打印库名时指定默认库
日志里没记录默认数据库、导致 EXPLAIN EXTENDED 失败,用 --database 补上默认库:
pt-table-usage /var/log/mysql/slow.log --database appdb场景:只分析某个库的查询
日志里混杂多个库,只想看 appdb 的表依赖,用 --filter 按 db 过滤事件:
pt-table-usage /var/log/mysql/slow.log \
--filter '($event->{db} || "") eq "appdb"'功能说明
日志应为 MySQL 慢查询日志格式。
"表使用"不只是查询读写了哪些表,还包括数据流(data in / data out): 工具根据表在查询中出现的上下文判断数据流。一条查询可以同时在多个上下文中使用同一张表, 输出会列出每张表的每个上下文,这份"上下文-表"清单描述了数据如何在表之间流动。
工具把数据流分析到列级别,因此查询中的列最好能无歧义地标识: 单表查询没有困难;多表查询且列名未加表前缀时,需要 EXPLAIN EXTENDED + SHOW WARNINGS 来确定列属于哪张表。若日志中未打印默认数据库,EXPLAIN EXTENDED 可能失败, 此时可用 --database 指定默认数据库,或用 --create-table-definitions 消除歧义。
输出
为每条查询中的每张表打印使用报告,例如:
Query_id: 0x1CD27577D202A339.1
UPDATE t1
SELECT DUAL
JOIN t1
JOIN t2
WHERE t1第一行是查询 ID(默认与 pt-query-digest 报告中的一致,即查询"指纹"的 MD5 校验值), 格式为 查询ID.表编号。表编号对多表 UPDATE 查询中每张被更新的表递增 1 (其他类型查询恒为 1;不支持多表 DELETE)。
其后的"上下文-表"行含义如下:
- SELECT:查询从该表取数据——或作为结果集返回(SELECT 查询必有), 或流向其他表(INSERT/UPDATE 的数据来源)。不来自任何表的值以
DUAL表示 (如字面量 "bar"、42、NOW() 等,可用--constant-data-value更改该名称)。 - 其他动词(INSERT/UPDATE/DELETE 等):表示查询以某种方式修改该表的数据; 若其后跟着 SELECT 上下文,则说明数据从 SELECT 的表流向该表(如
INSERT..SELECT)。 不支持 SET、LOAD 和多表 DELETE。 - JOIN:被连接的表(显式 JOIN 或 WHERE 中的隐式连接,如
t1.id = t2.id)。 - WHERE:WHERE 子句中用于过滤的表(不含隐式连接,隐式连接归入 JOIN)。 只列去重后的表。
- TLIST:查询访问但不属于其他上下文的表,通常是隐式笛卡尔积 (如
SELECT * FROM t1, t2中无连接条件的 t1、t2)。
退出状态
出错时退出码为 1,无错误为 0。
选项
| 选项 | 说明 |
|---|---|
--ask-pass | 连接 MySQL 时交互式询问密码 |
--[no]buffer-stdout | 默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭 |
-A, --charset | 类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8) |
--config | 类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置 |
--constant-data-value | 类型:string;默认:DUAL。常量数据(字面量、函数值等非表来源数据)打印为的"表名" |
--[no]continue-on-error | 默认:yes。出错时继续工作 |
--create-table-definitions | 类型:array。从这些文件读取 CREATE TABLE 定义(可保存 mysqldump --no-data 的输出);用于限定表/列名;跨库同名表等歧义无法解决 |
--daemonize | fork 到后台并与 shell 脱离,仅限 POSIX 系统 |
-D, --database | 类型:string。默认数据库 |
-F, --defaults-file | 类型:string。只从给定文件读取 mysql 选项,必须给绝对路径 |
--explain-extended | 类型:DSN。执行 EXPLAIN EXTENDED 的服务器,用于消解未限定的列/表名歧义 |
--filter | 类型:string。丢弃该 Perl 代码不返回 true 的事件(与 pt-query-digest 的 filter 相同机制,可为代码串或代码文件) |
--help | 显示帮助并退出 |
-h, --host | 类型:string。要连接的主机 |
--id-attribute | 类型:string。用该属性标识事件;默认为查询 ID(指纹的 MD5) |
--log | 类型:string。daemonize 时把全部输出打印到该文件 |
-s, --mysql_ssl | 类型:int。创建 SSL MySQL 连接 |
-p, --password | 类型:string。连接密码(含逗号需转义) |
--pid | 类型:string。创建指定的 PID 文件;冲突规则与自动清理见官方文档 |
-P, --port | 类型:int。连接端口 |
--progress | 类型:array;默认:time,30。向 STDERR 打印进度:`percentage |
--query | 类型:string。分析该指定查询而不是读日志文件 |
--read-timeout | 类型:time;默认:0。等待输入事件的最长时间,0 表示永远等;超时后停止读取并打印报告。需要 Perl POSIX 模块 |
--run-time | 类型:time。运行多久后退出;默认永远运行(可用 Ctrl-C 中断) |
--set-vars | 类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000;无法设置时打印警告并继续 |
-S, --socket | 类型:string。连接使用的 socket 文件 |
-u, --user | 类型:string。登录用户(若非当前用户) |
--version | 显示版本并退出 |
DSN 选项
| 键 | DSN 部分 | 说明 |
|---|---|---|
A | charset | 默认字符集 |
D | — | 默认数据库(不复制) |
F | mysql_read_default_file | 只从给定文件读取默认选项(不复制) |
h | host | 要连接的主机 |
p | password | 连接密码(含逗号需转义) |
P | port | 连接端口 |
S | mysql_socket | 连接使用的 socket 文件(不复制) |
u | user | 登录用户(若非当前用户) |
s | mysql_ssl | 创建 SSL 连接 |
其他信息
作者:Daniel Nichter
通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明。
更多细节请阅读 官方文档。