窗口函数使用会话级临时表内存,由tmp_table_size与max_heap_table_size的较小值限制,超限即落盘;其内存不走innodb_buffer_pool_size或sort_buffer_size,而是用于构建窗口临时表以缓存排序行、帧边界及聚合状态。

窗口函数在 MySQL 8.0 中不共享全局缓冲池,而是为每个查询会话动态分配独立的内存空间,且这部分内存来自 tmp_table_size 和 max_heap_table_size 的限制范围,超出即落盘 —— 这是性能断崖的主因。
窗口函数用的是哪块内存?
它不走 innodb_buffer_pool_size,也不用 sort_buffer_size 做排序(那是 ORDER BY 单独用的)。窗口函数执行时,MySQL 内部会创建一个“窗口临时表”,用于暂存分组后已排序的行、维护帧边界、缓存聚合中间状态。这块内存从会话级临时表内存池中划拨,上限由以下两个参数共同决定:
-
tmp_table_size:内存中临时表的最大字节数 -
max_heap_table_size:HEAP 表单表上限,取二者较小值作为实际限额
注意:即使你把 tmp_table_size 设得很大,如果 max_heap_table_size 更小,窗口函数仍会按后者截断。
为什么 EXPLAIN 看不到内存用量?
EXPLAIN 只显示是否用临时表(Using temporary),但完全不透露用了多少内存、是否溢出。真正判断是否落盘,得靠运行时指标:
- 查
SHOW STATUS LIKE 'Created_tmp_disk_tables':该值在窗口查询后跳增,说明已写磁盘 - 看慢日志里的
Rows_examined是否远大于结果集行数:这是帧扫描放大效应,常伴随磁盘临时表 - 监控
Handler_read_rnd_next:飙升说明正在随机读取磁盘临时表,性能已崩
分区数据量大时,内存怎么被切分?
窗口函数不会为每个 PARTITION BY 分桶单独分配固定内存,而是按需扩张 —— 执行器先尝试把当前分区所有行塞进内存临时表,若超限就整体落盘;后续分区复用同一套落盘结构,不再重试内存模式。这意味着:
- 最大分区的数据量决定整条 SQL 的内存压力,不是平均分区大小
- 哪怕 99 个分区都很小,只要有一个分区含 50 万行且
tmp_table_size仅设 64M,整个窗口计算就会走磁盘 -
ORDER BY字段无索引时,每分区都要做一次 filesort,而排序过程本身也消耗额外会话内存(sort_buffer_size),和窗口内存叠加
容易被忽略的内存叠加点
一个典型误判是认为“建了索引就万事大吉”。实际上,即使 PARTITION BY + ORDER BY 走了索引免排序,窗口函数仍要分配内存来维护帧(比如 ROWS BETWEEN 10 PRECEDING AND CURRENT ROW 需缓存最多 11 行状态)。更隐蔽的是:
- SELECT 列越多,临时表行宽越大,同样行数下更快触达
tmp_table_size上限 - 多个窗口函数共用同一
PARTITION BY + ORDER BY时,只做一次排序,但各自维护独立帧状态,内存占用是线性叠加的 -
RANGE帧比ROWS更吃内存:前者需在内存中保留所有同ORDER BY值的行用于去重比较,后者只存物理位置邻近的行
所以调大 tmp_table_size 前,先确认帧定义是否必要 —— 很多移动平均场景其实可以用 ROWS 替代 RANGE,省下一半内存。











