EXPLAIN (ANALYZE, BUFFERS) 是唯一可靠入口,必须同时启用ANALYZE获取Actual Total Time和磁盘写入证据,启用BUFFERS观察shared read/hit比例以判断work_mem是否足够,缺一不可。

EXPLAIN (ANALYZE, BUFFERS) 是唯一可靠入口
只用 EXPLAIN 看不到窗口函数真实开销——它只显示预估 cost,而 WindowAgg 节点的 width 和实际内存压力必须靠真实执行才能暴露。不加 ANALYZE,你根本不知道它到底有没有写磁盘;不加 BUFFERS,也看不出 shared hit/read 比例,无法判断 work_mem 是否够用。
常见错误现象:EXPLAIN 显示 cost 很低,但查询跑起来卡住、top 看到 PostgreSQL 进程在疯狂刷磁盘,或者日志里出现 WARNING: writing block XXX of temporary file。
- 必须用
EXPLAIN (ANALYZE, BUFFERS),不能省略任一选项 - 如果查询太慢不敢直接跑,先加
LIMIT 1000缩小数据集再分析 - 注意看输出里
WindowAgg节点下的Actual Total Time和Buffers: shared read行数——read 多说明溢出到磁盘了
重点盯死 WindowAgg 节点的 width 和 cost
WindowAgg 节点的 width 值超过 1000 字节,基本等于给内存埋雷。这个 width 不是输入行宽,而是窗口计算过程中每行要暂存的字段总大小——比如你在 SELECT 里拖了个 jsonb 字段,又没在 PARTITION BY 或 ORDER BY 里用它,它照样被缓存进窗口分区。
容易踩的坑:OVER () 看似没分区没排序,但 PostgreSQL 仍可能插入一个 Sort 节点来保证结果确定性;这时 WindowAgg 的 cost 会突然飙升,而你完全没写 ORDER BY。
- 检查
width:如果比上游节点(如Seq Scan)的 width 翻倍以上,立刻查SELECT列表和PARTITION BY字段,删掉冗余大字段 - 检查
cost:若显著高于上游节点,看它下面是否挂了Sort或HashAggregate——前者说明隐式排序被触发,后者说明分区键没索引 - 确认业务是否真需要严格顺序:如果只是要
COUNT(*) OVER(),可改用ORDER BY ctid避免无谓排序
PARTITION BY 字段没索引,窗口就成内存黑洞
没有索引的 PARTITION BY 字段(比如 user_id),PostgreSQL 只能先把全表数据读进内存哈希分组,或全部排序后切片。一旦数据量超 work_mem,立刻生成临时文件,Buffers: shared read 会猛增,查询卡在 “Writing to disk” 阶段。
性能影响非常直接:100 万行、500 个 user_id 分区,没索引时可能占 2GB 临时空间;建完 B-tree 索引后,WindowAgg 下的 Sort 消失,Actual Total Time 从 8 秒降到 0.6 秒。
- 高频使用的
PARTITION BY字段,哪怕单列也要建 B-tree 索引 - 如果是表达式分区,比如
PARTITION BY date_trunc('month', created_at),必须建函数索引:CREATE INDEX idx_orders_month ON orders (date_trunc('month', created_at)) - 别指望优化器自动“感知”物理顺序:即使表按
user_id插入,没CLUSTER或对应索引,它照样不认为已排序
MySQL 8.0 的窗口执行计划怎么看
MySQL 的 EXPLAIN 不像 PostgreSQL 那样显式标出 WindowAgg,但它会在 Extra 列里暴露关键信号:Using filesort 出现在窗口函数相关行,说明触发了排序;Using temporary 则代表内存不够、用了磁盘临时表。
MySQL 没有 BUFFERS 选项,所以没法直接看内存/磁盘比例,得靠间接指标:比如 rows_examined 暴涨、created_tmp_disk_tables 计数上升,或者 SHOW STATUS LIKE 'Created_tmp%' 在查询前后对比。
- 执行前先
SET profiling = 1,跑完用SHOW PROFILES看耗时分布 - 重点关注
Extra列是否含Using filesort或Using temporary,这是窗口函数内存压力最直接的提示 - MySQL 8.0.22+ 支持
EXPLAIN FORMAT=tree,能看到更清晰的窗口操作层级,比如WINDOW FUNCTION节点下挂的SORT或AGGREGATE
窗口函数的内存消耗藏得深,不是看语句长得简单就安全;真正危险的是那些没显式 ORDER BY 却偷偷排序、PARTITION BY 字段看着普通却没索引、SELECT 里混着大字段还浑然不觉的情况。

















