窗口函数带“using temporary”是因为mysql需构建内存临时结构缓存排序行、维护帧边界及聚合状态,其内存上限由tmp_table_size与max_heap_table_size中较小值决定,且受最大分区数据量、字段宽度影响,超限则落盘。

窗口函数为什么总带 Using temporary
Using temporary 不是错误提示,而是 MySQL 正在为窗口构建内存临时结构——它要缓存已排序的行、维护帧边界(比如 ROWS BETWEEN 10 PRECEDING AND CURRENT ROW)、保存聚合中间状态。这个临时表不走 innodb_buffer_pool_size,也不用 sort_buffer_size,而是从会话级临时表内存池里划拨,上限由 tmp_table_size 和 max_heap_table_size 中较小值决定。
- 即使你给
tmp_table_size设了 256M,只要max_heap_table_size是 64M,窗口函数最多就只能用 64M 内存 -
SELECT中列越多、字段越宽(比如大量TEXT或长VARCHAR),同样行数下越容易触达内存上限 -
EXPLAIN完全不显示用了多少内存,只告诉你“用了临时表”;真想确认是否落盘,得看SHOW STATUS LIKE 'Created_tmp_disk_tables'是否跳增
PARTITION BY 分区越大,整条 SQL 越容易落盘
窗口函数不会按分区均分内存,而是「逐个处理分区 + 按需扩张」:执行器先尝试把当前分区所有行塞进内存临时表,超限就整体落盘;后续分区复用同一套磁盘结构,不再重试内存模式。
- 决定是否落盘的不是平均分区大小,而是最大分区的数据量
- 举例:100 个部门,99 个只有 100 行,但有一个含 50 万行,且
tmp_table_size仅设 64M → 整个查询必然写磁盘临时表 - 如果
ORDER BY字段没索引,每个分区还得额外做一次 filesort,这部分又消耗sort_buffer_size,和窗口内存叠加 —— 容易被忽略的双重压力
为什么有索引还看到 Using filesort
Using filesort 在窗口函数里是正常行为,不代表低效。它表示 MySQL 正在为 PARTITION BY + ORDER BY 准备排序上下文,而且只发生一次:全表扫描或按索引顺序读一遍,之后所有窗口计算都基于这个已排序流。
- 复合索引必须覆盖
PARTITION BY和ORDER BY字段,例如(department, salary DESC) - 仅建
INDEX(salary)无法利用ORDER BY salary DESC,MySQL 8.0+ 才支持降序索引,得显式声明INDEX(salary DESC) - 即使免排序,窗口函数仍需内存维护帧(比如缓存前 10 行),所以建了索引 ≠ 内存压力消失
子查询对比下,临时表开销更隐蔽但更可控
关联子查询每执行一次,都可能独立触发临时表创建、WHERE 过滤、filesort —— 10 万行外层数据,就可能生成 10 万次小临时表,其中若干次因结果集稍大直接落盘。
- 窗口函数的临时表是「全局一次性构建」,数据只进一次,框架复用
- 子查询的临时表是「按需反复创建」,每次都要分配内存、排序、释放,IO 和 CPU 更碎片化
- 但窗口函数的内存瓶颈更集中:一个大分区撑爆
tmp_table_size,整条 SQL 就崩;子查询则可能只是局部慢,不容易被监控捕获
真正卡点不在语法本身,而在最大分区的数据量、字段宽度、以及两个内存参数的实际生效值——这三个变量叠在一起,才决定临时表是待在内存里,还是连夜写进 temp_N.ibt。











