MySQL 临时表之所以会写入磁盘,通常不是单纯因为数据量过大,而更多是由于参数配置不合理、索引设计不足,或 SQL 写法不佳所导致。要减少 MySQL 内部临时表落盘问题,建议重点监控 Created_tmp_disk_tables 的占比,并结合调大 tmp_table_size 与 max_heap_table_size、避免使用 TEXT/BLOB 等大字段、持续优化 SQL 语句等方法综合处理。

大多数 MySQL 内部临时表落盘,并不是因为业务数据真的特别大,而是配置参数、索引结构或 SQL 语句触发了“必须写磁盘”的条件。解决这类问题的核心思路是:尽量让临时表停留在内存中,或者从执行计划层面避免临时表生成。
先确认是否真的发生了磁盘临时表写入
不要凭感觉判断,先查看状态指标。执行 SHOW STATUS LIKE 'Created_tmp%',重点关注以下两个状态值:
Created_tmp_tables:创建的临时表总数,包含内存临时表和磁盘临时表Created_tmp_disk_tables:其中写入磁盘的临时表数量
如果 Created_tmp_disk_tables 的占比超过 5%,通常就值得开始排查和优化。需要注意的是,这类统计更适合用来观察实例或会话阶段性行为,服务重启后会清零,因此最好结合监控系统进行长期跟踪分析。
适当调大内存临时表的容量上限
MySQL 会取 tmp_table_size 和 max_heap_table_size 中较小的那个值,作为内存临时表可使用的最大空间。一旦临时表超过这个限制,哪怕只是多出 1 字节,也会立即转为磁盘临时表。
- 这两个参数最好设置为相同值,否则很容易因为取较小值而出现意外落盘
- 如果设置过小(例如默认 16M),中等规模的
GROUP BY或ORDER BY就可能直接写磁盘 - 如果设置过大(例如超过 2G),在高并发场景下可能带来 OOM 风险;更稳妥的做法是从 64M 开始,根据
Created_tmp_disk_tables的变化趋势逐步上调 - 临时调整可使用:
SET GLOBAL tmp_table_size = 67108864(64M),但要确认账号具备 SUPER 权限,且该修改在重启后会失效;如需永久生效,应写入 my.cnf
避免使用容易触发落盘的字段类型和 SQL 写法
以下几种常见情况,会让 MySQL 无法继续使用 Memory 引擎,从而强制创建磁盘临时表:
- 查询中的
SELECT或GROUP BY涉及TEXT、BLOB、JSON字段——即使只引用其中一列,也可能导致整个临时表落盘 - 使用
SELECT *从宽表读取数据,尤其当表中包含大字段时,更容易超过内存临时表限制 ORDER BY与GROUP BY作用在不同列上,且没有合适的复合索引覆盖时,优化器往往会先创建临时表完成排序,再执行分组- 在
IN()中传入大量值时,MySQL 可能会在内部构建哈希临时结构;当哈希桶数量较多时,也可能进一步写入磁盘
优化方式其实并不复杂:优先显式列出真正需要的字段,避免无意义的全列查询;对大字段可按业务需要改为 VARCHAR(1000) 这类截断读取方式;针对 ORDER BY 和 GROUP BY 的常用组合建立复合索引;如果业务允许,尽量使用 UNION ALL 替代 UNION,从而避免去重带来的临时表开销。
将 tmpdir 调整到独立的高速存储目录
即使有一部分临时表无法避免写盘,也不建议让它们落在系统盘或默认的 /tmp 目录中,尤其是在 tmpfs 内存挂载场景下更要谨慎。很多时候,真正的性能瓶颈并不是磁盘空间不够,而是 I/O 队列被打满。
- 先通过
SELECT @@tmpdir查看当前临时目录路径,常见结果是/tmp或空值(表示使用系统默认目录) - 创建新的目录,例如
/data/mysql-tmp,并确保mysql用户具备读写权限 - 在 my.cnf 的
[mysqld]配置段中加入tmpdir = /data/mysql-tmp,重启 MySQL 后生效 - 配合执行
chown mysql:mysql /data/mysql-tmp和chmod 755,避免因权限问题导致报错
一个经常被忽略的细节是:修改 tmpdir 后,一定要再次执行 SELECT @@tmpdir 确认新路径已经生效,同时还要检查该目录的 inode 是否充足,可通过 df -i 查看;否则即使磁盘空间足够,临时文件数量过多时依然可能创建失败。
