Skip to content

pt-visual-explain

把 MySQL 的 EXPLAIN 输出格式化为查询执行计划树。

语法

bash
pt-visual-explain [OPTIONS] [FILES]

把 EXPLAIN 输出转换为查询计划的树形表示。给定 FILE 时从文件读取输入; 不指定 FILE 或 FILE 为 - 时读标准输入。

bash
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 的输出存成文件,直接转成直观的执行计划树:

bash
pt-visual-explain explain_output.txt

场景:从 mysql 客户端管道进来

现场排查时,一边用 mysql -e 出 EXPLAIN,一边直接管道给工具看树:

bash
mysql -e "EXPLAIN SELECT * FROM mysql.user" | pt-visual-explain

场景:让工具连库直接解释一条查询

把待分析的查询存成文件,用 -c 让工具自己连库执行 EXPLAIN(会读取 .my.cnf,一般无需额外连参):

bash
pt-visual-explain -c query.sql

场景:指定主机/用户连库解释

要在指定实例上解释某条查询,显式给连接信息(密码由 .my.cnf 或交互式 --ask-pass 提供,不要写明文):

bash
pt-visual-explain -c --host db.example.com -u admin query.sql

场景:InnoDB 表避免多余书签查找

被分析的表是 InnoDB(主键即聚簇),加 --clustered-pk 让工具不再为 PRIMARY KEY 访问生成书签查找节点,计划更贴近实际:

bash
pt-visual-explain --clustered-pk explain_output.txt

场景:输出 dump 格式调试

想看解析后的原始数据结构(而非树),用 --format dump 输出 Data::Dumper 形式:

bash
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

查询计划为左深、深度优先遍历,树的根是输出节点(执行计划的最后一步)。上例读法:

  1. film 表做全表扫描,预计访问 952 行。
  2. 对每一行,用 sakila.film.film_id 的值对 film_actor->idx_fk_film_id 索引做查找(index lookup), 再对 film_actor 表做书签查找(bookmark lookup)。

算法(简述)

树由每行的 idselect_typetable 列构建:

  • id 是 SELECT 的顺序编号(不表示嵌套,类似正则表达式捕获括号的编号); 相邻两行 id 相同则以单次扫描多连接(single-sweep multi-join)方式连接。
  • select_typeSIMPLE(无子查询/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 部分说明
Acharset默认字符集
Ddatabase默认数据库
Fmysql_read_default_file只从给定文件读取默认选项
hhost要连接的主机
ppassword连接密码(含逗号需转义)
Pport连接端口
Smysql_socket连接使用的 socket 文件
uuser登录用户(若非当前用户)
smysql_ssl创建 SSL 连接

其他信息

  • 通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明

更多细节请阅读 官方文档

Percona Toolkit 中文文档 · 社区维护的第三方学习站