mysql通过show global status统计累计创建的内部临时表数量,关键指标为created_tmp_tables(内存)和created_tmp_disk_tables(磁盘),比值超10%需警惕;可用performance_schema定位高频建表sql。

怎么查 MySQL 里到底建了多少内部临时表
MySQL 不会直接暴露“当前有多少内部临时表在运行”,但会统计自启动以来的累计创建次数,这个指标藏在 SHOW GLOBAL STATUS 里,关键看 Created_tmp_disk_tables 和 Created_tmp_tables 这两个值。
前者代表落到磁盘的临时表数量(性能杀手),后者是内存中创建的总次数。两者的比值如果持续高于 10%,基本可以断定有查询在频繁触发磁盘临时表。
-
Created_tmp_disk_tables高 ≠ 一定有问题,但高且Created_tmp_tables也高,说明大量查询设计不合理 - 注意:该计数不会重置,除非重启 MySQL 或执行
FLUSH STATUS - 想看实时变化?用两次
SHOW GLOBAL STATUS LIKE 'Created_tmp%'间隔几秒取差值
哪些 SQL 容易触发磁盘临时表
不是所有 ORDER BY 或 GROUP BY 都会建临时表,但满足以下条件时,MySQL 很可能放弃内存、转而写磁盘:
- 排序字段没有索引,且结果集超过
tmp_table_size和max_heap_table_size中的较小值 -
GROUP BY+ORDER BY字段不一致,比如GROUP BY a ORDER BY b(无法复用索引) - 使用了
SELECT DISTINCT且字段无法走索引覆盖 - 子查询中含
UNION,或外层 JOIN 的 ON 条件太宽泛导致中间结果膨胀
典型错误现象:EXPLAIN 输出里出现 Using temporary; Using filesort,尤其是同时出现这两项时,90% 以上会走磁盘临时表。
如何快速定位是哪条 SQL 在狂建临时表
靠慢日志不够——很多问题 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小,说明单次就非常重
如果 performance_schema 关闭或版本太老(slow_query_log + log_queries_not_using_indexes = ON 组合排查,但漏报率高。
tmp_table_size 和 max_heap_table_size 改多大才合适
这两个值必须设成一样,否则以小的那个为准。设太大可能撑爆内存,设太小又频繁落盘——没有固定值,得看实际负载:
- 线上 OLTP 系统建议从
64M起步,观察Created_tmp_disk_tables / Created_tmp_tables比值是否降到 5% 以下 - 不要盲目调到 256M+,尤其当
innodb_buffer_pool_size不足时,内存争抢会导致更严重的抖动 - 临时表大小判断发生在语句执行前,所以即使中间结果只占 10MB,只要预估超限,就会直接走磁盘
- 修改后需重启连接才生效(已建立的连接仍用旧值)
真正难的是区分“该优化 SQL”还是“该调参数”。多数时候,把 ORDER BY 字段加上索引,比把 tmp_table_size 从 32M 改成 128M 更有效,也更安全。










