pt-visual-explain
把 MySQL 的 EXPLAIN 输出格式化为查询执行计划树。
语法
pt-visual-explain [OPTIONS] [FILES]把 EXPLAIN 输出转换为查询计划的树形表示。给定 FILE 时从文件读取输入; 不指定 FILE 或 FILE 为 - 时读标准输入。
pt-visual-explain <file_containing_explain_output>
pt-visual-explain -c <file_containing_query>
mysql -e "explain select * from mysql.user" | pt-visual-explain用法示例
本工具把 EXPLAIN 输出渲染成树。多数场景从文件或管道读取现成的 EXPLAIN 文本,不需要连库;只有用 -c/--connect 让工具自己连库执行 EXPLAIN 时才需要连接信息。命令里的文件路径、查询串、主机替换成你自己的即可。
场景:把 EXPLAIN 文本文件渲染成树
已经抓到一条 EXPLAIN 的输出存成文件,直接转成直观的执行计划树:
pt-visual-explain explain_output.txt场景:从 mysql 客户端管道进来
现场排查时,一边用 mysql -e 出 EXPLAIN,一边直接管道给工具看树:
mysql -e "EXPLAIN SELECT * FROM mysql.user" | pt-visual-explain场景:让工具连库直接解释一条查询
把待分析的查询存成文件,用 -c 让工具自己连库执行 EXPLAIN(会读取 .my.cnf,一般无需额外连参):
pt-visual-explain -c query.sql场景:指定主机/用户连库解释
要在指定实例上解释某条查询,显式给连接信息(密码由 .my.cnf 或交互式 --ask-pass 提供,不要写明文):
pt-visual-explain -c --host db.example.com -u admin query.sql场景:InnoDB 表避免多余书签查找
被分析的表是 InnoDB(主键即聚簇),加 --clustered-pk 让工具不再为 PRIMARY KEY 访问生成书签查找节点,计划更贴近实际:
pt-visual-explain --clustered-pk explain_output.txt场景:输出 dump 格式调试
想看解析后的原始数据结构(而非树),用 --format dump 输出 Data::Dumper 形式:
pt-visual-explain --format dump explain_output.txt功能说明
pt-visual-explain 把 EXPLAIN 输出逆向还原为查询执行计划,并格式化为左深树(left-deep tree) ——与 MySQL 内部表示计划的方式相同。手工阅读 EXPLAIN 需要耐心与专业知识, 树形表示对多数人更易理解。
- 解析输入:支持三种格式——mysql 客户端的表格式、
G结尾的垂直格式、制表符分隔格式; 无法解析的行会被忽略。 - 执行输入(需
--connect):把输入中第一个 SELECT 之前的内容替换为EXPLAIN SELECT, 然后连接 MySQL 执行。
两种方式都从结果集构建树并打印到标准输出。例如查询 select * from sakila.film_actor join sakila.film using(film_id) 的计划:
JOIN
+- Bookmark lookup
| +- Table
| | table film_actor
| | possible_keys idx_fk_film_id
| +- Index lookup
| key film_actor->idx_fk_film_id
| possible_keys idx_fk_film_id
| key_len 2
| ref sakila.film.film_id
| rows 2
+- Table scan
rows 952
+- Table
table film
possible_keys PRIMARY查询计划为左深、深度优先遍历,树的根是输出节点(执行计划的最后一步)。上例读法:
- 对
film表做全表扫描,预计访问 952 行。 - 对每一行,用
sakila.film.film_id的值对film_actor->idx_fk_film_id索引做查找(index lookup), 再对film_actor表做书签查找(bookmark lookup)。
算法(简述)
树由每行的 id、select_type、table 列构建:
id是 SELECT 的顺序编号(不表示嵌套,类似正则表达式捕获括号的编号); 相邻两行 id 相同则以单次扫描多连接(single-sweep multi-join)方式连接。select_type:SIMPLE(无子查询/UNION)、PRIMARY(最外层 SELECT)、[DEPENDENT] UNION(与前一结果 UNION)、UNION RESULT(结束一组 UNION 结果)、[DEPENDENT|UNCACHEABLE] SUBQUERY(新子作用域,WHERE/SELECT 列表中的子查询)、DERIVED(FROM 子句中的派生表)。JOIN 的多张表 select_type 相同。table通常是表名或别名,也可能是<derivedN>(对 id 为 N 的子查询临时表的前向引用) 或<unionN,...>(对被 UNION 的 select 的后向引用)。- 顺序很重要:某行 id 小于前一行,说明它依赖更早的较小 id 的行(如上面的子查询示例)。
工具通过重排序处理前向/后向引用,找出 UNION/DERIVED 作用域的边界来递归建树; 对非派生子查询则依赖 MySQL 深度优先执行的假设。完整算法说明见官方文档。
模块复用
该程序是可运行的 Perl 模块,内嵌 ExplainParser(解析 EXPLAIN 文本)与 ExplainTree(把行集转为树)两个包,方便单元测试或在你的代码中复用解析与建树功能。
选项
| 选项 | 说明 |
|---|---|
--ask-pass | 连接 MySQL 时交互式询问密码 |
--[no]buffer-stdout | 默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭 |
-A, --charset | 类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8) |
--clustered-pk | 假定 PRIMARY KEY 索引访问无需书签查找即可取回行(InnoDB 即如此) |
--config | 类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置 |
-c, --connect | 把输入当作查询:连接 MySQL 实例执行 EXPLAIN;配合 --user 等连接选项;有 .my.cnf 时自动读取 |
-D, --database | 类型:string。连接的数据库 |
-F, --defaults-file | 类型:string。只从给定文件读取 mysql 选项,必须给绝对路径 |
--format | 类型:string;默认:tree。输出格式:tree(简洁美化树)、dump(Data::Dumper 输出) |
--help | 显示帮助并退出 |
-h, --host | 类型:string。要连接的主机 |
-s, --mysql_ssl | 类型:int。创建 SSL MySQL 连接 |
-p, --password | 类型:string。连接密码(含逗号需转义) |
--pid | 类型:string。创建指定的 PID 文件;冲突规则与自动清理见官方文档 |
-P, --port | 类型:int。连接端口 |
--set-vars | 类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000;无法设置时打印警告并继续 |
-S, --socket | 类型:string。连接使用的 socket 文件 |
-u, --user | 类型:string。登录用户(若非当前用户) |
--version | 显示版本并退出 |
DSN 选项
| 键 | DSN 部分 | 说明 |
|---|---|---|
A | charset | 默认字符集 |
D | database | 默认数据库 |
F | mysql_read_default_file | 只从给定文件读取默认选项 |
h | host | 要连接的主机 |
p | password | 连接密码(含逗号需转义) |
P | port | 连接端口 |
S | mysql_socket | 连接使用的 socket 文件 |
u | user | 登录用户(若非当前用户) |
s | mysql_ssl | 创建 SSL 连接 |
其他信息
- 通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明。
更多细节请阅读 官方文档。