开发者工具 · 数据库

SQL EXPLAIN 可视化

MySQL/PG EXPLAIN 输出图形化

✓本地处理 · 不上传 ✓免费 · 无需登录 ✓无次数限制 累计 123 次使用
粘贴即自动识别 MySQL / PostgreSQL · 实时解析 · 全本地
EXPLAIN 输出paste MySQL / PostgreSQL EXPLAIN · 支持 ANALYZE / FORMAT JSON
待解析 在上方粘贴 EXPLAIN 输出,或点顶部「示例」按钮。
等待 EXPLAIN 输入 — 自动识别数据库并可视化执行计划
第一节

关于本工具

About

面对几十行EXPLAIN输出,眼睛扫过type、key、rows,却很难在脑中合成一条查询的完整执行路径。这个工具把MySQL和PG的文本计划解析成节点树,用连线与颜色标出全表扫描、索引查找、临时表等关键步骤,rows与cost直接叠在节点旁。粘贴输出即刻出图,数据留在浏览器,不经过任何服务器。

使用场景

壹

慢查询根因定位

线上 MySQL 一个查询突然耗时从 50ms 飙到 3s,DBA 值班收到告警。拿到 EXPLAIN 输出后,面对 rows 估算值、Extra 中的 Using filesort 和 Using temporary,纯文本难以快速判断瓶颈。本工具将执行计划转为树形图,一眼看出驱动表选择错误、索引失效导致的全表扫描,以及排序操作在哪个阶段发生,直接定位到需要加联合索引的字段组合。

贰

索引设计效果验证

开发同学给一张 500 万行订单表加了 (status, create_time) 联合索引,不确定是否最优。他分别跑带索引和不带索引的 EXPLAIN,输出文本长达 20 行,对比 type 从 ALL 变成 ref、key_len 从 NULL 变成 5,但无法直观确认回表次数。本工具可视化展示 ref 列和 filtered 百分比,同时高亮 Extra 中的 Using index condition,帮助确认索引下推生效,避免冗余索引。

叁

多表 JOIN 顺序优化

一个报表查询 JOIN 了 5 张表,每次跑 8 秒。开发用 EXPLAIN 看到第一张驱动表扫描了 200 万行,但不确定是否该换小表驱动。纯文本里 nested-loop 的嵌套层级不直观。本工具将 JOIN 顺序渲染成树状层级,每个节点标注 rows 和 filtered,直观比较不同驱动表选择下的总扫描行数,最终将小表(2000 行)作为驱动表,查询降到 0.3 秒。

肆

分区表查询剪枝验证

运维将一张 1 亿行的日志表按月份做了 RANGE 分区,查询 WHERE create_time BETWEEN '2024-01-01' AND '2024-01-31'。EXPLAIN 输出中 partitions 字段显示 p202401,但无法确认是否真的只扫描了一个分区。本工具图形化展示分区裁剪结果,用不同颜色标记被扫描和未被扫描的分区,一眼确认查询只命中 1 个分区,而非全表 12 个分区,验证分区设计有效。

伍

子查询改写效果对比

一个 EXISTS 子查询在 PG 上执行 2 秒,开发尝试改写成 JOIN 后 EXPLAIN 输出变长,但不确定改写是否真的更优。纯文本里两个计划的 cost 值相差不大,难以判断。本工具支持同时对比两个 EXPLAIN 的可视化结果,并排展示执行计划树,高亮 cost 最高的节点和 rows 偏差最大的步骤,直观看出改写后减少了两次 Seq Scan,总 cost 降低 40%。

第二节

使用指南

Getting Started

使用步骤

  1. 1在输入框粘贴 MySQL 或 PostgreSQL 的 EXPLAIN 输出文本,粘贴后自动解析并生成流程图
  2. 2鼠标悬停在流程图任一节点上,高亮显示该操作的预估行数、过滤条件与成本值
  3. 3点击节点右上角「详情」图标,展开该步骤的完整执行计划 JSON 原文与解释说明
  4. 4拖动流程图空白区域平移视图,滚动滚轮缩放,聚焦到耗时最长的节点

输入输出示例

输入输出说明
EXPLAIN SELECT * FROM users WHERE id = 1;【树形图】 ┌──────────────┐ │ Index Scan │ │ (cost=0.28..8.30 rows=1 width=100) │ │ Index Cond: (id = 1) │ └──────────────┘ 【表格】 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |----|-------------|-------|-------|---------------|------|---------|-------|------|-------| | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |常规:主键等值查询,展示最简单的 Index Scan 树形图和 const 类型表格,验证基础解析正确性
EXPLAIN SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.status = 'active';【树形图】 ┌─────────────────────┐ │ Hash Join │ │ (cost=12.50..45.20 rows=200 width=200) │ │ Hash Cond: (o.customer_id = c.id) │ ├─────────────────────┤ │ Seq Scan on orders │ │ (cost=0.00..20.10 rows=1000 width=150) │ ├─────────────────────┤ │ Hash │ │ (cost=10.00..10.00 rows=100 width=50) │ │ └─ Seq Scan on customers │ │ (cost=0.00..10.00 rows=100 width=50)│ │ Filter: (status = 'active'::text) │ └─────────────────────┘ 【表格】 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |----|-------------|----------|------|---------------|------|---------|-----|------|-------| | 1 | SIMPLE | customers| ALL | NULL | NULL | NULL | NULL| 100 | Using where | | 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL| 1000 | Using where; Using join buffer (Hash Join) |常规:两表 Hash Join,展示树形嵌套结构和表格多行输出,验证 join 类型解析
EXPLAIN SELECT COUNT(*) FROM logs WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';【树形图】 ┌───────────────────────────┐ │ Aggregate │ │ (cost=1500.00..1500.01 rows=1 width=8) │ │ └─ Seq Scan on logs │ │ (cost=0.00..1400.00 rows=40000 width=0) │ │ Filter: ((created_at >= '2023-01-01'::date) AND (created_at <= '2023-12-31'::date)) │ └───────────────────────────┘ 【表格】 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |----|-------------|-------|------|---------------|------|---------|-----|------|-------| | 1 | SIMPLE | logs | ALL | idx_created | NULL | NULL | NULL| 40000| Using where |边界:聚合函数 COUNT 且无索引覆盖,展示 Seq Scan + Aggregate 组合,验证聚合查询的图形化表达
EXPLAIN SELECT * FROM huge_table WHERE non_indexed_column = 'test';【树形图】 ┌───────────────────────────┐ │ Seq Scan on huge_table │ │ (cost=0.00..50000.00 rows=500000 width=200) │ │ Filter: (non_indexed_column = 'test'::text) │ └───────────────────────────┘ 【表格】 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |----|-------------|------------|------|---------------|------|---------|-----|------|-------| | 1 | SIMPLE | huge_table | ALL | NULL | NULL | NULL | NULL|500000| Using where |边界:无索引列过滤,rows 预估 50 万行,暴露全表扫描的高成本,帮助用户识别索引缺失
EXPLAIN SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 WHERE status = 1);【树形图】 ┌───────────────────────┐ │ Nested Loop │ │ (cost=0.00..100.00 rows=10 width=100) │ ├───────────────────────┤ │ Seq Scan on t1 │ │ (cost=0.00..20.00 rows=1000 width=100)│ ├───────────────────────┤ │ Index Scan on t2 │ │ (cost=0.00..0.08 rows=1 width=4) │ │ Index Cond: (id = t1.id) │ │ Filter: (status = 1) │ └───────────────────────┘ 【表格】 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |----|-------------|-------|-------|---------------|------|---------|-----------|------|-------| | 1 | PRIMARY | t1 | ALL | NULL | NULL | NULL | NULL | 1000 | | | 2 | DEPENDENT SUBQUERY | t2 | index | NULL | PRIMARY | 4 | func | 1 | Using where |易错:子查询被优化为 DEPENDENT SUBQUERY(相关子查询),每行执行一次,性能差;用户常误以为子查询只执行一次
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;{ "query_block": { "select_id": 1, "cost_info": { "query_cost": "1.00" }, "table": { "table_name": "users", "access_type": "const", "possible_keys": ["PRIMARY"], "key": "PRIMARY", "key_length": "4", "rows_examined_per_scan": 1, "rows_produced_per_join": 1, "filtered": "100.00", "cost_info": { "read_cost": "0.00", "eval_cost": "0.10", "prefix_cost": "0.00", "data_read_per_join": "100" }, "used_columns": ["id", "name", "email"] } } }边界:FORMAT=JSON 输出,展示工具对 JSON 格式的解析能力,验证 JSON 树形图渲染
EXPLAIN SELECT * FROM (SELECT * FROM a UNION ALL SELECT * FROM b) AS sub;【树形图】 ┌───────────────────────┐ │ Append │ │ (cost=0.00..40.00 rows=2000 width=100) │ ├───────────────────────┤ │ Seq Scan on a │ │ (cost=0.00..10.00 rows=1000 width=100)│ ├───────────────────────┤ │ Seq Scan on b │ │ (cost=0.00..10.00 rows=1000 width=100)│ └───────────────────────┘ 【表格】 | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | |----|-------------|-------|------|---------------|------|---------|-----|------|-------| | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL| 2000 | | | 2 | DERIVED | a | ALL | NULL | NULL | NULL | NULL| 1000 | | | 3 | UNION | b | ALL | NULL | NULL | NULL | NULL| 1000 | |易错:UNION ALL 子查询,展示 Append 节点和 DERIVED/UNION 类型,用户常混淆 UNION 与 UNION ALL 的成本差异

常见错误对照

1.EXPLAIN 输出未包含完整查询计划

✗ 错误EXPLAIN SELECT * FROM users WHERE id = 1;
✓ 修复EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;

默认 EXPLAIN 输出表格格式,本工具依赖 JSON 结构解析。MySQL 8.0+ 支持 FORMAT=JSON,PG 需用 EXPLAIN (FORMAT JSON)。缺少该选项导致解析失败或图形错乱。

2.混用 MySQL 与 PostgreSQL 的 EXPLAIN 输出

✗ 错误将 MySQL 的 EXPLAIN 表格文本直接粘贴到 PG 模式的解析器
✓ 修复确认数据库类型后,使用对应格式输出:MySQL 用 FORMAT=JSON,PG 用 EXPLAIN (ANALYZE, FORMAT JSON)

MySQL 和 PG 的 EXPLAIN 输出结构完全不同(字段名、嵌套层级、统计信息格式)。工具按数据库类型选择解析器,混用会导致节点缺失或连线错误。

3.EXPLAIN 语句末尾缺少分号

✗ 错误EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'active'
✓ 修复EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'active';

MySQL 客户端要求每条 SQL 以分号结束。缺少分号时,客户端可能不发送语句或报语法错误,工具收不到任何输出。

4.将 ANALYZE 输出当作普通 EXPLAIN 粘贴

✗ 错误EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1; 的输出(含实际执行时间、行数)
✓ 修复EXPLAIN (ANALYZE, FORMAT JSON) SELECT * FROM users WHERE id = 1; 的输出

ANALYZE 会实际执行查询,输出中包含大量运行时统计信息(如 actual time、loops)。这些字段在 JSON 结构中的位置与普通 EXPLAIN 不同,工具可能无法正确映射到图形节点。

5.粘贴了带注释或前缀的完整 SQL 日志

✗ 错误/* 查询用户订单 */ EXPLAIN FORMAT=JSON SELECT ... ; # 2024-01-01
✓ 修复EXPLAIN FORMAT=JSON SELECT ... ;

工具只解析 EXPLAIN 输出中的 JSON 部分。SQL 注释、时间戳、日志前缀等非 JSON 文本会破坏解析器,导致无法生成图形。

6.EXPLAIN 输出包含多行文本而非纯 JSON

✗ 错误复制 MySQL Workbench 中格式化后的 EXPLAIN 表格(含列标题、分隔线、多行文本)
✓ 修复在 MySQL 命令行执行 EXPLAIN FORMAT=JSON ... 后,复制输出的单行 JSON 字符串

表格格式的 EXPLAIN 输出是文本排版,不是结构化 JSON。工具需要完整的 JSON 对象才能解析节点和连接关系。图形化展示依赖 JSON 的嵌套结构。

7.使用旧版 MySQL 的 EXPLAIN EXTENDED 输出

✗ 错误EXPLAIN EXTENDED SELECT * FROM users; 的输出(MySQL 5.6 及更早)
✓ 修复EXPLAIN FORMAT=JSON SELECT * FROM users; (MySQL 5.7+)

EXPLAIN EXTENDED 在 MySQL 5.7 已废弃,其输出不包含 JSON 格式。工具要求 JSON 结构,旧版输出只能显示部分信息,图形节点不完整。

8.EXPLAIN 输出中包含非 ASCII 字符或 BOM

✗ 错误从 Windows 记事本保存的 EXPLAIN 输出文件,开头含 UTF-8 BOM (EF BB BF)
✓ 修复使用纯文本编辑器(VS Code、Sublime)保存为 UTF-8 without BOM 格式后复制

BOM 字符会被 JSON 解析器视为非法字符,导致解析失败。工具后端解析 JSON 时,BOM 会引发 SyntaxError,无法生成任何图形。

第三节

工作原理

How It Works

核心公式

cost = rows × (cpu_operator_weight + io_block_cost / 1000)

变量说明

  • cost查询执行代价,无量纲
  • rows估算扫描行数,来自 EXPLAIN
  • cpu_operator_weightCPU 算子权重,如 0.2
  • io_block_costIO 块代价,默认 10

示例

EXPLAIN 输出 rows=1000,MySQL 默认 cpu_operator_weight=0.2,io_block_cost=10:cost = 1000 × (0.2 + 10/1000) = 1000 × 0.21 = 210。图形化中节点大小按 cost 比例缩放,210 比 50 大 4.2 倍。

EXPLAIN 原文SQL 文本解析 & 拆分提取节点 / 类型 / 代价识别关联子句布局计算树形坐标映射连线路径生成图形输出SVG 树图校验:语法 / 方言不合法 → 报错提示
用户输入 本地处理 输出结果 校验 / 错误分支
第五节

常见问题

Q & A
问这个工具支持 Oracle 的 EXPLAIN 输出吗?我贴进去没反应。+
答

本工具目前仅支持 MySQL 和 PostgreSQL(PG)的 EXPLAIN 输出格式。Oracle、SQL Server、SQLite 等其他数据库的 EXPLAIN 语法和输出结构差异较大,无法正确解析。如果粘贴后无图形展示,可检查顶部提示,确认输出开头是否为 mysql> 或 EXPLAIN 标准格式。如果是 Oracle,建议先用 EXPLAIN PLAN FOR 生成后,再用 DBMS_XPLAN.DISPLAY 输出文本,但本工具目前暂不兼容。

问为什么我贴了 EXPLAIN 但图里全是全表扫描,明明加了索引?+
答

图里显示全表扫描(type=ALL),说明优化器判断索引扫描成本更高,常见原因有:① 查询条件用了函数包裹列,如 WHERE DATE(create_time)='2024-01-01',索引失效;② 返回数据量占全表比例过大(通常 >20%),优化器认为走索引还不如全表快;③ 联合索引未遵循最左前缀原则。建议先将 EXPLAIN 输出粘贴到本工具,再对照图中 access_type 和 possible_keys 字段检查是否真走了索引。

问这个工具会把我的 SQL 或 EXPLAIN 结果上传到服务器吗?+
答

不会。本工具完全在浏览器端运行(FE 实现),你的 EXPLAIN 文本粘贴到输入框后,所有解析和图形绘制都在本地完成,不会发送到任何服务器。即使断开网络,页面加载后也能正常使用。隐私方面可以放心,敏感业务的查询计划无需担心外泄。

问图里 rows 显示 100 但实际查出来只有 10 行,是不是工具算错了?+
答

图里的 rows 是 MySQL 优化器估算的行数,不是实际查询结果的行数。优化器基于统计信息(如索引基数、表数据分布)估算,可能偏差很大,尤其在数据分布不均或统计信息过时时。实际行数需要用 EXPLAIN ANALYZE(MySQL 8.0.18+)才能看到。本工具忠实展示优化器估算值,不进行二次修正,所以看到 rows 和实际行数不一致是正常现象,可据此判断统计信息是否需要更新。

问EXPLAIN 输出里有个 Using temporary,图里怎么看具体哪个操作用了临时表?+
答

图里每个节点下方会显示 Extra 信息,如果某节点有 Using temporary,该节点会被标注黄色背景并附带“临时表”图标。常见于 GROUP BY 无索引、ORDER BY 与 GROUP BY 字段不一致、或 DISTINCT 与 JOIN 混用。点击该节点,右侧详情区会展开 Extra 字段完整内容,并提示可能优化的方向,比如建议增加联合索引覆盖排序和分组字段。

问我贴了 PG 的 EXPLAIN (ANALYZE, BUFFERS) 输出,但图里不显示实际时间和 buffer 信息?+
答

本工具支持 PG 的 EXPLAIN (ANALYZE, BUFFERS) 输出,但图形节点默认只展示核心字段(如 startup cost、total cost、rows、width)。实际时间(actual time)和 buffer 信息会折叠在节点下方的“详情”区域,需要点击节点展开查看。如果粘贴后完全没显示这些信息,请检查输出是否完整复制了从 QUERY PLAN 开始到最后一行的全部内容,不要遗漏末尾的 Planning Time 和 Execution Time 行。

问这个工具和 pt-query-digest 的 EXPLAIN 分析有什么区别?+
答

pt-query-digest 侧重慢查询日志的聚合统计和趋势分析,它会把多条 EXPLAIN 结果汇总成报告,但不生成可视化执行计划图。本工具专注于单条 EXPLAIN 输出的图形化展示,将访问类型、索引使用、临时表、排序等关键信息以节点树形式呈现,适合快速定位单条查询的执行瓶颈。两者互补:先用 pt-query-digest 找出最慢的查询,再用本工具可视化分析具体原因。

问为什么图里有些节点是红色的,有些是绿色的?+
答

颜色代表执行效率的预警等级。绿色节点表示该操作成本较低(如索引唯一扫描、主键查找),黄色表示存在潜在风险(如全表扫描但数据量不大、使用了临时表),红色表示高成本操作(如全表扫描且 rows 估算值很大、文件排序、使用了磁盘临时表)。颜色阈值基于 rows 和 cost 字段自动计算,并非固定数值,点击红色节点可查看具体成本值和优化建议。

隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。

↑↓选择 ↵打开 ⌘+↵新窗口 esc关闭