using temporary表示未走索引,mysql需建临时表分组排序,性能骤降;应确保where条件与group by字段顺序一致、索引覆盖所有查询字段、避免函数操作分组列。

EXPLAIN里出现Using temporary就说明没走索引
只要执行计划中Extra字段包含Using temporary或Using filesort,基本等于GROUP BY没用上索引。MySQL被迫在内存或磁盘建临时表、再排序分组,性能断崖下跌——百万行数据下,查询可能从几十毫秒跳到几秒甚至更久。
检查方式很简单:EXPLAIN FORMAT=TRADITIONAL SELECT ... GROUP BY ...,重点看三处:
-
type是否为ref或range,而不是ALL或index -
key是否显示你期望的索引名 -
key_len是否合理:比如VARCHAR(255)字段只用了前10个字节,但key_len显示765,说明索引定义和实际查询不匹配
索引字段顺序和GROUP BY顺序必须完全一致
MySQL只认最左前缀匹配,哪怕字段全对,顺序错一位,索引就废了。
常见错误写法:
-
GROUP BY dept_id, status,却建了INDEX idx_status_dept (status, dept_id)→ 无法跳过排序 -
WHERE org_id = ? GROUP BY dept_id, status,却只建(dept_id, status)→ 缺少过滤字段,仍需全扫
正确做法是把WHERE条件中高选择性的字段放最左,再严格按GROUP BY字段顺序追加:
-
WHERE org_id = ? AND dept_id = ? GROUP BY status→ 建INDEX idx_org_dept_status (org_id, dept_id, status) -
GROUP BY category, brand→ 建INDEX idx_cat_brand (category, brand),不能反过来
SELECT里混入非GROUP BY字段或聚合结果排序会强制回表或排序
这类写法表面合法,实则绕开索引能力:
-
SELECT dept_id, name, COUNT(*) FROM t GROUP BY dept_id→name不在GROUP BY中,MySQL必须回表取值,且大概率触发Using filesort -
GROUP BY user_id ORDER BY SUM(amount) DESC→SUM(amount)是计算结果,不在原始索引里,必然Using filesort
真正能走索引的只有两类情况:
- 纯分组+聚合,且SELECT字段全是GROUP BY列或聚合函数:如
SELECT user_id, COUNT(*) FROM t GROUP BY user_id - 覆盖索引包含所有字段:如建了
INDEX idx_user_amt (user_id, amount),查SELECT user_id, SUM(amount) GROUP BY user_id可避免回表
对分组字段用函数等于主动废掉索引
GROUP BY DATE(created_at)这种写法,无论created_at有没有索引,都白搭。MySQL无法用B+树索引直接定位函数结果,只能全表扫描后再计算。
替代方案有且仅有两个靠谱的:
- 加生成列并建索引:
ALTER TABLE t ADD COLUMN created_date DATE AS (DATE(created_at)) STORED,再建INDEX idx_created_date (created_date) - 改用范围查询代替函数分组:
WHERE created_at >= '2024-01-01' AND created_at (前提是<code>created_date已存在且有索引)
MySQL 8.0+虽支持函数索引CREATE INDEX idx_date ON t ((DATE(created_at))),但得先确认版本是否真支持,且函数索引不参与统计信息收集,优化器有时会误判成本。











