group by慢因索引失效,需检查explain中using temporary或using filesort;应按where+group by顺序创建联合索引,避免在分组字段上使用函数。

GROUP BY慢到卡住,先看EXPLAIN有没有Using temporary
只要执行计划里出现Using temporary或Using filesort,基本等于分组没走索引——MySQL被迫在内存或磁盘上建临时表、排序,性能断崖下跌。哪怕表只有百万行,这两个提示一出现,查询就可能从几十毫秒跳到几秒甚至更久。
实操建议:
- 用EXPLAIN FORMAT=TRADITIONAL SELECT ... GROUP BY ...确认执行路径
- 看type是否为ref/range,而不是ALL或index
- 检查key_len是否合理:比如字段是VARCHAR(255)但只用了前10个字节,key_len却显示765,说明索引定义和实际查询不匹配
建索引不是加单列,而是按WHERE + GROUP BY顺序联合建
单列索引对GROUO BY a有效,但对GROUP BY a, b几乎无效。真正起效的是最左前缀匹配的联合索引,且顺序必须和GROUP BY子句完全一致;如果还有WHERE条件,过滤性强的字段要放最左边。
常见组合示例:
- WHERE status = 'active' GROUP BY user_id → 建INDEX idx_status_user (status, user_id)
- GROUP BY category, brand → 建INDEX idx_cat_brand (category, brand)
- 如果SELECT里还包含sum(amount),且amount也常查,可扩展为(category, brand, amount)实现覆盖索引
别在GROUP BY里用函数,DATE(created_at)这种写法直接废掉索引
一旦对分组字段做函数操作,索引立刻失效。MySQL无法用索引定位DATE(created_at)的结果,只能全表扫描再计算。
替代方案:
- 提前在表里加冗余字段created_date DATE,并为其建索引
- 改用范围查询:WHERE created_at >= '2024-01-01' AND created_at <br>
- MySQL 8.0+ 可考虑函数索引(如<code>CREATE INDEX idx_date ON t ((DATE(created_at)))),但需确认版本支持
大表分组时tmp_table_size不够,会 silently 落盘成MyISAM临时表
MySQL默认把小临时表放内存,超限就落盘,I/O暴增。现象是SHOW PROCESSLIST里看到Coping to tmp table on disk,或者Created_tmp_disk_tables指标飙升。
调参注意点:
- tmp_table_size和max_heap_table_size必须同时调大,且取两者较小值生效
- 单次查询临时表大小上限受min(tmp_table_size, max_heap_table_size)限制
- 更稳妥的做法是加SQL_BIG_RESULT提示,让优化器提前选择哈希分组而非排序分组,减少内存压力
- 长期来看,优先靠索引和WHERE过滤缩小输入规模,比硬调参数更可靠
索引顺序、WHERE前置、函数避坑、临时表阈值——这四个点里,最容易被忽略的是WHERE和GROUP BY字段在联合索引里的相对位置,以及tmp_table_size和max_heap_table_size必须同步调整这个细节。











