“copying to tmp table on disk”出现即表明排序/聚合因内存不足被迫落盘,根因常是sort_buffer_size不足或索引缺失;需优先优化索引、避免大字段和隐式转换,而非盲目调大buffer。

查到“Copying to tmp table on disk”就等于定位到根因
这个状态在 SHOW PROFILE FOR QUERY N 或 Performance Schema 的 stage 事件里出现,说明排序/聚合过程已被迫落盘——不是临时表本身的问题,而是内存不够装下中间结果。它和 Created_tmp_disk_tables 上涨强相关,但比后者更早、更具体。
别只盯着 tmp_table_size,sort_buffer_size 才是排序阶段的直接内存配额。每个连接独占一份,设太大反而容易被 OS OOM killer 干掉,单实例建议不超过 4MB。
- 执行
SHOW VARIABLES LIKE 'sort_buffer_size';看当前值(默认常为 256K) - 用
EXPLAIN FORMAT=tree(MySQL 8.0+)确认是否走了索引排序;若显示Using filesort且 type=ALL,优先修索引,而不是加 buffer - 检查 SQL 是否含隐式转换:比如
WHERE status = '1'对 INT 字段查询,会导致索引失效,进而触发全表扫描+强制排序落盘
为什么调大 sort_buffer_size 有时没用?
因为 MySQL 不会把全部 sort_buffer_size 都用来排序。它只在“能预估结果集大小”的前提下分配内存,一旦发现字段含 TEXT、BLOB 或 JSON,会直接跳过内存排序,强制走磁盘临时表——此时调 buffer 完全无效。
- 用
SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'db' AND DATA_TYPE IN ('text', 'blob', 'json');快速扫一遍涉及表的大字段 - 如果排序字段本身是
VARCHAR(2000)且实际存了长文本,即使没声明 TEXT,也可能触发落盘 -
ORDER BY字段必须和WHERE条件共用同一索引最左前缀,否则 buffer 再大也没机会用上
如何快速验证是不是 sort_buffer_size 导致的落盘?
开 profiling 后跑一次问题 SQL,再看 profile 输出里 Copying to tmp table on disk 这一行的耗时占比。如果占比超过总耗时 30%,基本可以锁定。
- 先执行
SET profiling = 1; - 复现问题 SQL
- 执行
SHOW PROFILES;找到对应 Query ID - 执行
SHOW PROFILE FOR QUERY N;,重点看 stages 列中Copying to tmp table on disk是否存在、耗时是否异常高 - 对比改小
sort_buffer_size(如设为 64K)再跑一次,若该阶段耗时暴涨,说明原值虽不够但确实在起作用
比调参更值得优先做的三件事
临时表落盘本质是中间结果太大,而 buffer 只是兜底。真正省资源的方式是让 MySQL 根本不用算那么多。
- 给
ORDER BY和GROUP BY字段建联合索引,且确保 WHERE 条件能命中该索引前缀 - 避免 SELECT *,尤其不要把 TEXT 字段拖进排序;改用明确字段列表 + 聚合前 LIMIT 控制数据量
- 拆分复杂 UNION 查询,或用
WITH RECURSIVE/WITH ... AS MATERIALIZED(MySQL 8.0.23+)控制中间结果生命周期
ibtmp1 文件不随重启自动收缩,但 Copying to tmp table on disk 出现频繁时,往往意味着某条 SQL 正在反复撑爆它——这时候盯住慢日志里带 Using temporary; Using filesort 的语句,比调 sort_buffer_size 有效得多。











