Created_tmp_disk_tables持续上涨是临时表过多的直接信号,意味着磁盘写入增加导致性能断崖式下跌;ORDER BY未走索引、SELECT *、字段类型不匹配、GROUP BY无ORDER BY NULL等均会触发临时表落盘。

临时表过多的直接信号是 Created_tmp_disk_tables 持续上涨
不是所有临时表都危险,MySQL 默认优先用内存(tmp_table_size 和 max_heap_table_size 共同控制),但一旦中间结果超出阈值,就会落盘——这时性能断崖式下跌。PHP 层看到的“慢”,往往就是卡在磁盘 I/O 上。关键指标不是 Created_tmp_tables,而是 Created_tmp_disk_tables:每增加 1,基本意味着一次磁盘写入。
ORDER BY 没走索引时必然触发 Using filesort 或 Using temporary
EXPLAIN 看到这两项中的任意一个,就说明 MySQL 没法靠索引顺序返回数据,得自己排序或建临时结构。常见原因包括:
-
ORDER BY字段没索引,或索引顺序不匹配(比如索引是(status, created_at),却写ORDER BY created_at DESC) - WHERE 条件用了非等值查询(如
WHERE created_at > '2024-01-01'),导致后续ORDER BY列无法复用索引有序性 - SELECT 中包含大字段(如
TEXT、JSON),即使有索引,优化器也可能放弃索引排序,改用全字段排序+临时表 - PHP 里拼 SQL 时加了
ORDER BY RAND(),强制全表扫描和临时表
GROUP BY 字段类型不一致或 SELECT * 会隐式放大临时表
这是 PHP 场景下最隐蔽的坑:ORM 自动生成的 SELECT * + GROUP BY user_id,实际只用 user_id 和 COUNT(*),却把 bio、avatar_url 这类大字段也拖进分组流程,极大膨胀临时表体积。更糟的是:
- 字段类型不匹配(比如
user_id是INT,但 WHERE 中传了字符串'123'),导致索引失效,退化为全表扫描+临时表 - 关闭了
ONLY_FULL_GROUP_BY,却写了SELECT name, COUNT(*) FROM users GROUP BY dept_id—— MySQL 不报错,但必须建临时表来随机取name值 - 没加
ORDER BY NULL,MySQL 默认对分组结果再做一次隐式排序,多一次Using filesort
PHP 层能做的最有效干预:提前过滤 + 显式指定字段 + 缓存聚合结果
索引优化是基础,但 PHP 层不配合,再好的索引也白搭。重点不是“怎么写 GROUP BY”,而是“能不能不查原表”:
- 把
WHERE条件尽可能下推到子查询或视图里,而不是在 PHP 循环里foreach ($rows as $r) { if ($r['status'] !== 'active') continue; } - 禁用
SELECT *,明确写出需要的字段,尤其避开大文本字段参与分组或排序 - 报表类查询(如“各城市订单数”)不要每次请求都扫
orders表,PHP 层先查 Redis,键如stats:orders_by_city:202604,过期设为 1 小时 - 如果必须实时计算,考虑用生成列(
ALTER TABLE orders ADD COLUMN city_code VARCHAR(10) AS (LEFT(address, 10)) STORED)+ 索引,避免GROUP BY SUBSTRING(address, 1, 10)这类函数运算
真正难处理的从来不是单条 SQL,而是 PHP 把多个未过滤的分组查询串成链式调用,中间结果层层叠加。监控 Created_tmp_disk_tables 的增长节奏,比看单条 EXPLAIN 更能暴露问题本质。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











