Internal_tmp_table_created仅统计成功创建内部临时表的次数,不区分内存/磁盘,且不记录后续任何IO操作;真实IO压力需看Created_tmp_disk_tables和file_summary_by_instance中#sql相关写入字节数。

Internal_tmp_table_created 是什么,为什么它不等于实际临时表IO
Internal_tmp_table_created 是 MySQL 5.7+ Performance Schema 中 events_statements_summary_by_digest 或状态变量里一个常被误读的计数器。它只统计「成功创建内部临时表」的次数,不区分内存还是磁盘;更关键的是,它**不记录临时表后续的读写IO**——哪怕一张临时表在磁盘上反复扫描10次,这个值也只加1。
真正反映IO压力的是:Created_tmp_disk_tables(状态变量)和 io_global_by_file_by_bytes 中匹配 /tmp/#sql_* 或 /var/lib/mysql/#sql_* 的行。前者告诉你有多少次被迫落盘,后者告诉你具体刷了多少字节。
- 当
Created_tmp_disk_tables持续增长,而Created_tmp_tables增长缓慢,说明很多查询在内存够用时仍因字段类型/排序逻辑被强制落盘 - MySQL 8.0 默认
internal_tmp_mem_storage_engine = TempTable,但VARCHAR超过 64KB、TEXT/BLOB字段、或GROUP BY含非索引列时,依然会 fallback 到InnoDB临时表并写磁盘 -
tmp_table_size和max_heap_table_size必须同时调大才有效,且仅对 MEMORY 引擎起作用;对 TempTable 引擎无效
如何定位具体哪条 SQL 触发了磁盘临时表
靠 SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables' 只能知道“有”,不能知道“是谁”。必须结合 Performance Schema 实时抓取:
SELECT DIGEST_TEXT, COUNT_STAR, SUM_DISK_USAGE_TOTAL FROM performance_schema.events_statements_summary_by_digest WHERE SUM_DISK_USAGE_TOTAL > 0 ORDER BY SUM_DISK_USAGE_TOTAL DESC LIMIT 5;
注意:SUM_DISK_USAGE_TOTAL 是 MySQL 8.0.22+ 才支持的字段,低于该版本需退回到传统方式:
- 开启
log_output = 'TABLE'+slow_query_log = ON+log_queries_not_using_indexes = ON - 设置
long_query_time = 0并过滤 slow log 表mysql.slow_log中含Using temporary; Using filesort的行 - 对可疑 SQL 手动
EXPLAIN FORMAT=TREE,重点看-> Temporary table with deduplication或-> Using temporary table是否出现在物化节点下
常见触发磁盘临时表的SQL模式及绕过方法
不是所有 GROUP BY 或 DISTINCT 都危险,关键看执行计划是否能利用索引消除排序/去重步骤。以下模式极易中招:
-
SELECT DISTINCT a,b FROM t ORDER BY c:即使a,b有联合索引,ORDER BY c无索引 → 强制建临时表再排序 → 若c是TEXT,立刻落盘 -
SELECT * FROM t1 JOIN t2 ON ... GROUP BY t1.id, t2.category:若t2.category无索引,MySQL 无法在 join 过程中流式分组,必须缓存全部结果后聚合 -
UNION ALL很安全,但UNION(隐式DISTINCT)在无索引字段上会建临时表去重,尤其当子查询返回大量VARCHAR(255)字段时
绕过核心思路:用覆盖索引把排序/分组动作“推到存储层”。例如将 ORDER BY c 改为 ORDER BY c LIMIT 10 并给 (c, a, b) 建联合索引,可跳过临时表。
监控脚本要盯住的三个真实指标
别再只刷 SHOW GLOBAL STATUS LIKE '%tmp%'。生产环境应持续采集以下三项,并设告警阈值(例如每分钟 > 50):
-
Created_tmp_disk_tables:每分钟增量。突增直接对应慢查询或新上线SQL -
Performance_schema.file_summary_by_instance中FILE_NAME包含#sql且WRITE_BYTES > 0的记录数及总字节数 -
information_schema.INNODB_METRICS中innodb_buffer_pool_reads是否同步上升——若临时表IO高但 buffer pool 读不涨,说明是纯磁盘临时表,没走缓冲池
临时表IO的隐蔽性在于:它不走 query cache,不触发 binlog event,甚至可能绕过某些审计插件。最易被忽略的是分区表上的 ALTER TABLE ... ORDER BY ——这种 DDL 内部会建巨型临时表,且不会出现在 statement digest 统计里。











