应监控created_tmp_disk_tables每秒增长是否超1,超则说明临时表拖垮io;需启用performance_schema实时捕获,查sum_created_tmp_disk_tables大户;mysql 8.0.16+需调大temptable_max_ram,默认1gb易致性能骤降;explain analyze可精准定位临时表生成环节。

直接看 Created_tmp_disk_tables 每秒增长是否超过 1,超了基本就是临时表在拖垮 IO;光查慢日志没用,高频轻量 SQL 才是真凶。
实时抓“谁在狂建临时表”
慢查询日志对高频率、单次快的语句完全失能。必须打开 performance_schema 实时捕获活跃行为:
- 先启用采集:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_statements_history_long', 'statements'); - 查最近磁盘临时表大户:
SELECT DIGEST_TEXT, COUNT_STAR, SUM_CREATED_TMP_TABLES, SUM_CREATED_TMP_DISK_TABLES FROM performance_schema.events_statements_summary_by_digest WHERE SUM_CREATED_TMP_DISK_TABLES > 0 ORDER BY SUM_CREATED_TMP_DISK_TABLES DESC LIMIT 5; -
SUM_CREATED_TMP_DISK_TABLES是该语句模板的历史累计值,不是单次;若COUNT_STAR小但该值极大,说明单次就重——比如一个GROUP BY JSON_EXTRACT(data, '$.tag')语句,每次都要解析并落盘
确认是不是 TEMPTABLE 引擎被卡内存池
MySQL 8.0.16+ 默认用 TempTable 引擎,但它不认 tmp_table_size,只看独立参数 temptable_max_ram。升级后性能骤降,八成是这个值还卡在默认 1GB(总内存的 3%)没调:
- 执行:
SELECT @@internal_tmp_mem_storage_engine, @@temptable_max_ram, @@tmp_table_size, @@max_heap_table_size; - 如果返回
TempTable且temptable_max_ram = 1073741824(即 1GB),而另外两个参数设到了 2G,那它根本用不满——TempTable只会在temptable_max_ram范围内分配内存,超了直接写ibtmp1 - 错误日志里出现
Writing temp table to disk或Using external sort是强信号
用 EXPLAIN ANALYZE 看清“哪一步真在建临时表”
普通 EXPLAIN 显示 Using temporary 只是预告,EXPLAIN ANALYZE 才告诉你它到底花了多少时间、占了多少内存、有没有被循环放大:
- 执行
EXPLAIN ANALYZE SELECT ...后,重点看缩进结构里带actual time=的行:如果某层Nested loop内部的actual time飙高,且Loops值等于驱动表行数,说明内层表被反复扫描建临时结果 - 看
Rows Removed by Filter:比如预估扫 10 万行,只留 200 行,大概率因WHERE字段没索引或选择性差,被迫把全量读进临时表再过滤 - 注意
Buffers: read=xxx:read值高,说明临时表数据没命中 buffer pool,要么调大innodb_buffer_pool_size,要么减少参与临时表的字段体积(别SELECT *)
真正难处理的是那些看起来“合理”的查询:加了索引、没 TEXT 字段、EXPLAIN 也干净,但一上量就爆 ibtmp1。这时候得盯住 temptable_max_ram 和 SQL 里所有表达式级操作——UPPER()、JSON_EXTRACT()、窗口函数,它们都会让 TempTable 放弃内存优化路径,直接走落盘预备模式。











