EXPLAIN FORMAT=JSON 怎样读取窗口节点
来源:17golang原创
时间:2026-10-25 10:58:41 311浏览 收藏
排查 MySQL 窗口函数时,传统 EXPLAIN 表格往往只能让你看到表访问路径,却看不清窗口计算本身。直接使用 EXPLAIN FORMAT=JSON,重点沿着 query_block.windowing.windows[] 读取,就能看到窗口函数、排序键和临时表信息。
官方地址:https://dev.mysql.com/doc/refman/8.4/en/window-function-optimization.html
windowing位于对应的query_block下,windows数组里的每个对象描述一个窗口处理组。functions说明计算了哪些窗口函数,using_filesort和filesort_key说明是否需要按窗口定义排序。using_temporary_table只能说明该窗口步骤使用了临时表,不能单独证明查询一定慢。
先让 EXPLAIN 输出窗口节点
先准备一个按部门分区、按薪资排序的查询。这里故意放入两个窗口函数,便于观察 windows 数组如何描述窗口计算:
-- 用一个稳定的员工编号作为并列薪资时的补充排序键。
EXPLAIN FORMAT=JSON
SELECT department_id, employee_id, salary,
ROW_NUMBER() OVER (
PARTITION BY department_id
ORDER BY salary DESC, employee_id
) AS department_rank,
SUM(salary) OVER (
PARTITION BY department_id
) AS department_total
FROM employee_salary
WHERE employment_status = 'active';
结果最外层通常是 query_block。窗口函数对应的执行信息在它的 windowing 节点中,继续向下看 windows 数组,而不是只盯着最外层的 table。

按 windows 数组读取一个窗口对象
官方示例中的窗口对象会出现 name、functions、using_filesort、filesort_key 和 using_temporary_table 等字段。可以按下面的顺序读:
| 字段 | 先回答的问题 | 阅读重点 |
|---|---|---|
name | 它对应哪个命名窗口? | 没有使用 WINDOW 命名时可能为空字符串。 |
functions | 这一组计算了什么? | 与 ROW_NUMBER、SUM 等窗口函数对照。 |
using_filesort | 是否需要排序? | 为分区和窗口 ORDER BY 准备顺序。 |
filesort_key | 按什么排序? | 和 PARTITION BY、窗口内 ORDER BY 的列或表达式核对。 |
using_temporary_table | 是否用了临时表? | 结合数据量、窗口帧和整体计划判断影响。 |
不要把数组下标直接当成“第几个 SELECT 输出列”。一个窗口对象描述的是一组窗口处理信息,functions 才是与计算内容直接对应的字段;后面的 table 或 nested_loop 仍要单独阅读。
{
"windowing": {
"windows": [
{
"name": "",
"using_temporary_table": true,
"using_filesort": true,
"filesort_key": ["`department_id`", "`salary` DESC"],
"functions": ["row_number"]
}
]
}
}
上面的 JSON 只用于说明字段形状,不应把示例中的布尔值当成每个查询都会得到的固定结果。实际字段以当前语句和版本输出为准。
结合临时表和窗口顺序判断代价
using_filesort: true 表示窗口步骤需要排序,它不是“必然慢”的结论;要继续看排序键、输入行数和其他访问路径。using_temporary_table: true 表示窗口处理中使用了临时表,涉及需要保存或重新访问行的窗口帧时更值得关注。
如果查询包含多个窗口,不要只数数组元素。MySQL 会关注不同窗口的分区和排序要求;排序要求相同的窗口可以连续处理,后续窗口可能复用已有顺序。改写 SQL 时,可以把重复的窗口定义写成命名窗口,减少表达式分散带来的阅读成本:
-- 给相同的分区和排序定义命名,避免多个 OVER() 各写一套表达式。
EXPLAIN FORMAT=JSON
SELECT department_id, employee_id, salary,
ROW_NUMBER() OVER department_window AS row_no,
RANK() OVER department_window AS salary_rank
FROM employee_salary
WHERE employment_status = 'active'
WINDOW department_window AS (
PARTITION BY department_id
ORDER BY salary DESC, employee_id
);

用固定路径做排查记录
实际对比改写前后的计划时,我会把记录拆成四层:先确认 query_block,再确认是否有 windowing,接着逐个读取 windows[],最后回到同一节点下的 table、nested_loop 和成本信息。这样能避免把“访问表很快”和“窗口排序很轻”混成一个结论。
-- 用同一条语句保存改写前后的 JSON 计划,便于逐字段比较。
EXPLAIN FORMAT=JSON
SELECT department_id, employee_id,
ROW_NUMBER() OVER (
PARTITION BY department_id
ORDER BY salary DESC, employee_id
) AS row_no
FROM employee_salary
WHERE employment_status = 'active';
如果没有看到 windowing,先确认当前语句真的含有窗口函数,并检查是否查看了正确的 query_block。如果有 windowing 但没有排序字段,不要自行补猜;只记录当前版本实际返回的字段。MySQL 文档明确指出,窗口执行信息过于丰富,传统输出无法完整展示,因此读取窗口节点应以 JSON 计划为主。
常见问题
为什么传统 EXPLAIN 看不到窗口函数的详细信息?
窗口处理信息较丰富,传统表格格式无法完整承载;应改用 EXPLAIN FORMAT=JSON 查看 windowing。
看到 using_filesort 就一定要改 SQL 吗?
不一定。它只说明该窗口步骤需要排序,还要结合排序键、输入规模和整体访问路径判断。
多个窗口对象是不是就代表执行了多个全量排序?
不能这样直接推断。排序要求相同的窗口可能连续处理并复用顺序,应比较每个对象的分区和排序字段。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
334 收藏
-
231 收藏
-
416 收藏
-
365 收藏
-
254 收藏
-
358 收藏
-
228 收藏
-
462 收藏
-
130 收藏
-
数据库 · MySQL | 1小时前 | MySQL · JSON · 数据校验 · JSON_SCHEMA_VALID CHECK 约束 MySQL JSON Schema JSON 结构校验 JSON_SCHEMA_VALIDATION_REPORT293 收藏
-
122 收藏
-
208 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习