不一定慢,using temporary仅表示创建内部临时表,内存中执行时开销小;真正导致性能骤降的是落盘,即触发copying to tmp table on disk或created_tmp_disk_tables计数上涨。

EXPLAIN 显示 Using temporary 就一定慢吗
不一定。Using temporary 只表示 MySQL 创建了内部临时表,但这个表可能还在内存里——真正拖慢查询的是“落盘”。关键看是否触发 Copying to tmp table on disk 或 Created_tmp_disk_tables 计数上涨。内存临时表(TempTable 或 MEMORY 引擎)开销很小;一旦写磁盘,I/O 成为瓶颈,延迟常翻几十倍。
什么条件会让内存临时表强制落盘
MySQL 比较 tmp_table_size 和 max_heap_table_size 的较小值,只要单行预估内存占用超过它,整个临时表就退化为磁盘表。这不是按总大小判断,而是按“最宽一行”估算:
- 字段定义过宽:比如
VARCHAR(2000),即使实际存 "a",MySQL 也按 2000 字节算 - 含
TEXT/BLOB类型:这类字段不能存内存临时表,直接走磁盘 - GROUP BY 或 ORDER BY 中字段长度超 512 字符(MySQL 5.6.15+)
- UNION 结果集中某列定义长度 > 512 字节
-
internal_tmp_disk_storage_engine设为InnoDB或MyISAM,但没配好对应日志/空间
ORDER BY 和 GROUP BY 怎么悄悄触发磁盘临时表
这两个操作本身不必然落盘,但常见写法会切断索引利用路径,迫使 MySQL 先拉全量数据再排序/分组,中间结果极易超标:
-
ORDER BY created_at DESC, user_id ASC—— MySQL 5.7 不支持混合方向索引,哪怕建了(created_at, user_id)也无效 -
GROUP BY DATE(created_at)—— 函数导致索引失效,MySQL 得把所有created_at值读出来再计算日期 -
WHERE status = 'paid' AND user_id = 123 GROUP BY region,但索引是(status, user_id),region不在索引里 - JOIN 后再
ORDER BY非驱动表字段,比如JOIN users ON orders.user_id = users.id ORDER BY users.name
怎么确认是哪条 SQL 在刷磁盘临时表
别靠 EXPLAIN 猜。真实线上问题往往藏在慢日志或 profiling 里:
- 开启全量抓取:
SET GLOBAL slow_query_log = ON,SET GLOBAL log_queries_not_using_indexes = ON,SET GLOBAL long_query_time = 0 - 查日志中带
Using temporary; Using filesort且执行时间长的语句 - 更准的方法:
SET profiling = 1,跑完查SHOW PROFILES,再对耗时高的 query 执行SHOW PROFILE FOR QUERY N,盯住Copying to tmp table on disk这一行的耗时占比 - 监控指标:
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables',结合业务高峰看是否突增
临时表落盘不是配置调大就能解决的事——很多 case 根子在 SQL 写法或索引缺失,调 tmp_table_size 只是掩盖问题,还可能挤占 innodb_buffer_pool_size。先 fix SQL,再调参,顺序错了代价很大。











