SQL EXPLAIN 可视化

MySQL/PG EXPLAIN 输出图形化

414 次访问

SQL EXPLAIN 可视化

MySQL / PostgreSQL EXPLAIN 输出图形化 · 自动标记可疑节点

粘贴 MySQL / PG EXPLAIN 输出(支持 EXPLAIN / EXPLAIN ANALYZE)

说明

· MySQL EXPLAIN 关键字段type(ALL=全表扫=坏 / range/ref/eq_ref/const=好)/ rows(扫描行数)/ Extra(Using filesort/temporary = 需优化)/ key(用到的索引)

· PG EXPLAIN ANALYZE:包含真实运行时间(actual time)· Seq Scan 全表 / Index Scan 索引 / Hash Join 等

关于本工具

了解工具定位 · 使用场景 · 对比优势

使用场景

🔍

慢查询根因定位

DBA 收到告警,某条业务 SQL 执行耗时从 50ms 飙升到 5s。将 EXPLAIN 输出粘贴到工具后,图形化展示显示:type 从 ref 退化为 ALL(全表扫描),rows 估算从 100 行变成 200 万行,Extra 列出现 Using filesort。DBA 立刻定位到索引被误删,无需逐行解析文本即可确定根因。

📊

索引效果验证

开发者在测试环境为订单表添加复合索引后,需要验证优化效果。分别导出加索引前后的 EXPLAIN 输出,工具并排渲染两张执行计划图。对比发现:possible_keys 从 NULL 变成 idx_order_date_status,key_len 从 NULL 变成 12,rows 从 50 万降到 800。图形化直观确认索引生效。

👥

团队 Code Review

技术主管在 PR 审核中看到一条复杂 JOIN 查询,文本 EXPLAIN 长达 30 行难以审阅。将输出粘贴到工具后,生成树形图清晰展示驱动表、嵌套循环顺序、临时表使用位置。主管在 10 秒内发现第 3 个 JOIN 使用了 Using join buffer (Block Nested Loop),指出需要调整表连接顺序。

🎓

SQL 优化教学

培训讲师讲解执行计划时,学员难以理解 type 列 ref/range/index/ALL 的差异。讲师将同一查询的不同写法(无索引、单列索引、复合索引)的 EXPLAIN 输出分别粘贴,工具并排显示 3 张执行计划图。学员直观看到 rows 从 100 万降到 1000、Extra 从 Using where 变为 Using index,理解索引对扫描范围的影响。

线上问题复盘

事故复盘会上,需要分析某次数据库 CPU 飙高根因。运维导出事故时间段的慢查询 EXPLAIN,粘贴到工具后生成执行计划图。图形显示多个查询的 type 为 ALL(全表扫描),且 rows 估算偏差超过 10 倍(实际 500 万行,估算 50 万行)。团队据此确认是统计信息过旧导致优化器选择错误执行计划。

对比矩阵本工具 vs 竞品 vs 传统方法

维度本工具 (explain-viz.tl654.com)pgMustard手工分析
数据隐私纯浏览器端处理,SQL 不上传服务器SQL 上传至云端服务器解析SQL 完全本地,无外传风险
处理速度提交后即时渲染,<1 秒网络请求 + 服务器解析,通常 2-5 秒人工逐行阅读计划树,数分钟至数十分钟
离线可用完全离线,打开页面后断网仍可用必须联网,服务器不可用时无法访问完全离线,依赖本地工具和人工
可视化形式树形图 / 流程图,直观展示执行顺序与成本带颜色编码的节点图,侧重成本占比纯文本表格,需脑补执行流程
学习成本零学习成本,粘贴即出图需理解其颜色和节点符号含义需掌握 EXPLAIN 输出中每个字段含义
大小限制受浏览器内存限制,普通查询(<1000 行计划)无问题受服务器限制,超大查询可能超时或被拒绝无限制,但人工处理超大计划极不现实
收费模式完全免费,无隐藏付费项免费版有限制(如每日查询次数),高级功能需订阅免费(仅需数据库客户端),但时间成本极高

使用指南

上手步骤 · 输入输出 · 避坑提示

输入输出示例7 个典型场景,覆盖常规、边界与易错

输入输出说明
EXPLAIN SELECT * FROM users WHERE id = 1;图形化树:Seq Scan on users (cost=0.00..35.50 rows=10 width=500) → 根节点典型常规场景:最简单的全表扫描查询
EXPLAIN SELECT * FROM orders o JOIN users u ON o.user_id = u.id WHERE u.status = 'active';图形化树:Hash Join (cost=10.00..200.00 rows=100) → Hash (cost=5.00..5.00 rows=50) → Seq Scan on users; Hash (cost=100.00..100.00 rows=1000) → Seq Scan on orders典型常规场景:两表 JOIN 查询,展示 Hash Join 节点
EXPLAIN FORMAT=JSON SELECT * FROM products WHERE category = 'electronics' AND price > 100;图形化树:Index Scan on products_category_idx (cost=0.25..8.27 rows=2 width=200) → Filter: (price > 100)边界 case:使用 FORMAT=JSON 格式输入,工具需正确解析
EXPLAIN ANALYZE SELECT * FROM logs WHERE created_at > '2023-01-01';图形化树:Seq Scan on logs (cost=0.00..5000.00 rows=100000 width=300) (actual time=0.012..150.000 rows=95000 loops=1)边界 case:包含 ANALYZE 实际执行时间信息
EXPLAIN SELECT * FROM users WHERE id IN (1, 2, 3, 4, 5);图形化树:Index Scan using users_pkey on users (cost=0.15..8.17 rows=5 width=500) → Index Cond: (id = ANY ('{1,2,3,4,5}'::integer[]))典型常规场景:使用 IN 条件的主键索引扫描
EXPLAIN SELECT * FROM huge_table ORDER BY random_column;图形化树:Sort (cost=10000.00..12000.00 rows=800000 width=200) → Seq Scan on huge_table (cost=0.00..5000.00 rows=800000 width=200)易错 case:无索引的排序操作,cost 极高,提示性能风险
EXPLAIN SELECT count(*) FROM users;图形化树:Aggregate (cost=35.50..35.51 rows=1 width=8) → Seq Scan on users (cost=0.00..35.50 rows=10 width=0)典型常规场景:聚合查询,展示 Aggregate 节点

常见错误对照8 个常踩的坑 · 错误 → 修复

1. 直接粘贴 EXPLAIN 的原始表格输出

错误
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|-------|------|---------------|-----|---------|-----|------|-------|
|  1 | SIMPLE      | users | ALL  | NULL          | NULL| NULL    | NULL| 10000| Using where |
修复
EXPLAIN SELECT * FROM users WHERE age > 18;

工具只解析 EXPLAIN 命令的原始输出文本(如 MySQL 的表格格式或 JSON 格式),而非 markdown 表格或格式化后的内容。直接粘贴表格会因格式不匹配导致解析失败。

2. 只粘贴 EXPLAIN 的一部分行

错误
1 SIMPLE users ALL NULL NULL NULL NULL 10000 Using where
修复
EXPLAIN SELECT * FROM users WHERE age > 18;

EXPLAIN 输出是结构化多行文本,每列有固定含义。只粘贴部分行或缺失列头会导致工具无法正确识别字段,进而无法生成图形。

3. 粘贴 EXPLAIN ANALYZE 的详细输出

错误
-> Filter: (age > 18)  (cost=1000 rows=1000) (actual time=0.123..5.456 rows=950 loops=1)
    -> Table scan on users  (cost=500 rows=10000) (actual time=0.010..3.200 rows=10000 loops=1)
修复
EXPLAIN SELECT * FROM users WHERE age > 18;

EXPLAIN ANALYZE 输出包含实际执行时间和行数,格式与标准 EXPLAIN 不同。本工具仅解析标准 EXPLAIN 的表格或 JSON 输出,ANALYZE 的文本结构会导致解析错误。

4. 输入非 EXPLAIN 命令的 SQL 语句

错误
SELECT * FROM users WHERE age > 18;
修复
EXPLAIN SELECT * FROM users WHERE age > 18;

工具需要的是 EXPLAIN 命令的输出,而不是原始 SQL 语句。直接输入 SELECT/INSERT 等语句,工具无法识别,必须先在数据库中执行 EXPLAIN 命令并粘贴其输出。

5. 粘贴 EXPLAIN FORMAT=JSON 但未包含完整 JSON

错误
{"query_block": {"select_id": 1, "table": "users"}}
修复
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE age > 18;

如果使用 FORMAT=JSON,必须粘贴完整的 JSON 输出(包含外层 query_block 和所有嵌套),而非手动提取的部分字段。工具依赖完整结构来解析节点关系和成本信息。

6. 混淆 MySQL 和 PostgreSQL 的 EXPLAIN 输出

错误
(粘贴 MySQL 的 EXPLAIN 表格输出到 PG 模式)
修复
(在工具中选择正确的数据库类型,或粘贴对应数据库的 EXPLAIN 输出)

MySQL 和 PostgreSQL 的 EXPLAIN 输出列名、格式和含义不同(如 MySQL 有 rows 列,PG 有 rows 和 width)。选错数据库类型会导致字段映射错误,图形节点信息不准确。

7. 粘贴包含控制字符或 ANSI 转义序列的输出

错误
EXPLAIN SELECT * FROM users;
修复
EXPLAIN SELECT * FROM users;

某些终端或工具(如 MySQL 客户端开启 --color 选项)会在输出中加入 ANSI 颜色代码。这些非打印字符会干扰解析器,导致工具无法正确识别 EXPLAIN 输出内容。

8. 粘贴 EXPLAIN 输出时包含多余的空行或注释

错误
EXPLAIN SELECT * FROM users;

| id | select_type | ... |
|----|-------------|-----|
|  1 | SIMPLE      | ... |
修复
| id | select_type | ... |
|----|-------------|-----|
|  1 | SIMPLE      | ... |

工具只解析 EXPLAIN 命令的输出部分,不解析命令本身或注释。粘贴时包含命令行或空行可能导致工具误判输入起点,建议只复制表格或 JSON 输出。

工作原理

公式推导 · 流程图解 · 依据出处

核心公式

Cost = Σ(node_cost) + Σ(join_cost) + Σ(filter_cost)

变量说明

  • Cost — 查询执行总代价(任意单位)
  • node_cost — 每个扫描/索引节点的 I/O 与 CPU 代价
  • join_cost — 连接操作的代价(嵌套循环/哈希/归并)
  • filter_cost — WHERE 条件过滤的行数评估代价

示例

EXPLAIN 输出:Seq Scan on users (cost=0.00..35.50 rows=2550 width=36)。其中 0.00 为启动代价,35.50 为总代价。该查询扫描全表 2550 行,每行代价约 0.014。若加上 JOIN:Hash Join (cost=12.25..47.80 rows=100 width=72),总代价 = 47.80,其中哈希构建代价 12.25,探测代价 35.55。

适用范围

适用于 MySQL 和 PostgreSQL 的 EXPLAIN 输出解析。代价单位是优化器内部估算值,非真实时间(毫秒/秒)。不同数据库(如 SQLite、Oracle)代价模型不同,不适用。

原理图

粘贴 EXPLAIN 输出MySQL / PG 原始文本浏览器内解析纯前端 · 无服务端请求可视化图形树状 / 流程图支持格式MySQL EXPLAINPostgreSQL EXPLAIN解析内容表扫描方式索引使用 / 连接类型可视化元素节点颜色标识类型箭头表示执行顺序
用户输入 本地处理 输出结果 详细说明

开发者集成

3 种主流语言 · 复制即用

import json
import subprocess

# 模拟 MySQL EXPLAIN 输出(实际从数据库获取)
explain_output = """
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | users | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1000 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
"""

# 解析 MySQL 文本表格格式
lines = explain_output.strip().split('\n')
headers = [h.strip() for h in lines[1].split('|')[1:-1]]
values = [v.strip() for v in lines[3].split('|')[1:-1]]

# 构建结构化数据
parsed = dict(zip(headers, values))
print(json.dumps(parsed, indent=2))
# 输出:{"id": "1", "select_type": "SIMPLE", "table": "users", ...}

# 生成可视化节点(简化版:用 Mermaid 流程图)
node = f"{parsed['id']}[{parsed['table']}]\n{parsed['id']}-->{parsed['type']}"
print(f"Mermaid 流程图:\n{node}")
package main

import (
	"encoding/json"
	"fmt"
	"strings"
)

// 模拟 PostgreSQL EXPLAIN (JSON 格式)
func main() {
	// 实际从数据库获取的 EXPLAIN (FORMAT JSON)
	rawJSON := `[
	  {
	    "Plan": {
	      "Node Type": "Seq Scan",
	      "Relation Name": "orders",
	      "Startup Cost": 0.00,
	      "Total Cost": 35.50,
	      "Plan Rows": 1000,
	      "Plan Width": 40
	    }
	  }
	]`

	// 解析 JSON 格式的 EXPLAIN 输出
	var plans []map[string]interface{}
	if err := json.Unmarshal([]byte(rawJSON), &plans); err != nil {
		panic(err)
	}

	plan := plans[0]["Plan"].(map[string]interface{})
	
	// 提取关键字段用于可视化
	fmt.Printf("节点类型: %s\n", plan["Node Type"])
	fmt.Printf("表名: %s\n", plan["Relation Name"])
	fmt.Printf("预估行数: %.0f\n", plan["Plan Rows"].(float64))

	// 生成 DOT 格式(Graphviz 可视化)
	dot := fmt.Sprintf(`digraph {
	  "%s" [label="%s\nrows: %.0f"];
	}`, plan["Relation Name"], plan["Node Type"], plan["Plan Rows"])
	fmt.Println("DOT 格式:\n" + dot)
}
// 浏览器端解析 MySQL EXPLAIN 文本格式
const explainText = `
+----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | range | idx_email     | idx_email | 1022   | NULL |  100 |   100.00 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
`;

// 解析表格行
const lines = explainText.trim().split('\n');
const headers = lines[1].split('|').slice(1, -1).map(h => h.trim());
const values = lines[3].split('|').slice(1, -1).map(v => v.trim());

const parsed = {};
headers.forEach((h, i) => { parsed[h] = values[i]; });

console.log('解析结果:', parsed);
// 输出: { id: '1', select_type: 'SIMPLE', table: 'users', type: 'range', ... }

// 生成 SVG 节点(简化版)
const svg = `<svg width="200" height="80">
  <rect x="10" y="10" width="180" height="60" rx="5" fill="#f0f0f0" stroke="#333"/>
  <text x="100" y="35" text-anchor="middle" font-size="14">${parsed.table}</text>
  <text x="100" y="55" text-anchor="middle" font-size="12" fill="#666">${parsed.type}</text>
</svg>`;
console.log('SVG 可视化:', svg);

常见问题

8 个高频疑问

我把 EXPLAIN 输出粘贴进去,为什么生成的图是空的或者显示解析错误?
最常见的原因是粘贴的内容包含了非标准格式。请确保粘贴的是 MySQL 的 `EXPLAIN FORMAT=TRADITIONAL` 或 PostgreSQL 的 `EXPLAIN` 原始文本输出,不要包含查询语句本身、执行时间行(如 `1 row in set (0.00 sec)`)或任何额外注释。另外,只支持单个查询的 EXPLAIN 结果,不支持同时粘贴多个查询的输出。如果仍然报错,可以尝试在 SQL 命令行中只复制 `id | select_type | table ...` 那几行表格数据,不包括表头分隔线。
为什么我看到的执行计划和同事用 Navicat 看到的顺序不一样?
这是常见现象。MySQL 的 EXPLAIN 输出行顺序是查询优化器决定的访问路径顺序,不同版本(如 5.7 与 8.0)、不同 optimizer_switch 设置、甚至表统计信息不同都可能导致顺序变化。本工具按行号顺序原样图形化展示,不会重新排序。如果顺序差异很大,建议检查两边的 MySQL 版本和索引状态是否一致,或者用 `EXPLAIN FORMAT=JSON` 获取更确定的执行计划后再对比。
这个工具和 MySQL Workbench 的 Visual Explain 有什么区别?
核心区别在于使用场景和依赖。MySQL Workbench 需要安装桌面客户端,且只支持连接到你本地或远程的 MySQL 实例才能获取执行计划。本工具完全在浏览器中运行,只需要粘贴 EXPLAIN 文本即可,适合快速排查线上问题(比如没有 Workbench 权限的服务器)、分享给同事看截图、或者嵌入到文档/博客中。缺点是它不显示实际的执行时间和行数,只展示逻辑计划结构。如果追求精准的性能诊断,还是建议用 Workbench 或 `EXPLAIN ANALYZE`。
图形化里那些箭头和方块分别代表什么意思?
每个方块代表 EXPLAIN 输出中的一行(即一个执行步骤),方块内的文字显示该步骤的访问类型(如 `ALL` 全表扫描、`ref` 非唯一索引查找)、使用的索引、预计行数等关键字段。箭头表示数据流向:从子步骤(下级方块)流向父步骤(上级方块),箭头上的标签(如果有)表示关联条件(如 `Using where`、`Using index condition`)。方块颜色按访问类型区分:`ALL` 全表扫描为红色警告,`index` 为黄色,`ref` 或 `eq_ref` 为绿色,`const` 为蓝色——红色方块通常是需要优化的重点。
我的 EXPLAIN 输出里有几十行,图形化后密密麻麻看不清怎么办?
本工具会自动根据行数调整布局:少于 10 行时使用紧凑树形图,超过 10 行时切换到可缩放画布模式,可以用鼠标滚轮缩放、拖拽平移。如果还是觉得乱,建议先排查是否有 `DEPENDENT SUBQUERY` 或 `DERIVED` 这类复杂子查询——它们会生成大量中间步骤。一个实用技巧:先只粘贴最外层查询的 EXPLAIN(去掉子查询),看清主干,再单独分析子查询部分。另外,可以点击任意方块,会在右侧高亮显示该步骤在原始 EXPLAIN 文本中的对应行。
工具里显示的预计行数(rows)准确吗?为什么和实际行数差很多?
不准确,而且这是 MySQL 本身的问题,不是本工具能解决的。EXPLAIN 中的 `rows` 列是优化器根据表统计信息(`SHOW TABLE STATUS` 中的 `Rows` 值)估算出来的,不是实际查询执行后的行数。当表数据分布不均匀、统计信息未及时更新(`ANALYZE TABLE` 后会有改善)、或者用了复杂的 WHERE 条件时,预估偏差可能达到 10 倍甚至更多。本工具只是原样展示这个值,不会做修正。如果需要真实行数,请使用 `EXPLAIN ANALYZE`(MySQL 8.0.18+)或 `SET profiling=1` 后看实际执行信息。
我粘贴了 PostgreSQL 的 EXPLAIN,但工具提示格式不支持,怎么办?
本工具同时支持 MySQL 和 PostgreSQL 格式,但需要确保粘贴的是标准文本格式。PostgreSQL 的 EXPLAIN 有两种常用输出格式:`TEXT`(默认)和 `JSON`、`XML`、`YAML`。本工具目前只支持 `TEXT` 格式,不支持 JSON 格式。请确认是否用了 `EXPLAIN (FORMAT JSON)`,如果是,改成 `EXPLAIN (FORMAT TEXT)` 或直接 `EXPLAIN` 即可。另外,PostgreSQL 的 EXPLAIN 输出中每个节点以 `->` 开头缩进表示层级,如果手动删除了缩进或对齐,也会导致解析失败。
断网后还能用吗?生成的图表能不能导出成图片?
可以断网使用。本工具所有逻辑(解析 + 渲染)都在浏览器本地完成,不需要联网。首次加载后,即使断开网络,刷新页面也能正常使用(浏览器会缓存静态资源)。目前支持右键点击画布选择“另存为图片”导出为 PNG,但注意:由于浏览器安全限制,导出的图片中文字可能在某些系统上显示为方框(字体缺失),建议在 Chrome 或 Edge 浏览器上操作。如果导出后文字不清晰,可以截图后使用系统自带的截图工具保存。
选择 打开 +新窗口 esc关闭