核心是让mysql利用索引有序性跳过排序和临时表,关键在于复合索引需严格匹配where、group by及select字段顺序,优先包含等值where列,并满足松散扫描条件以触发loose index scan。

核心是让 MySQL 能跳过排序和临时表,直接利用索引的有序性完成分组。关键不在“有没有索引”,而在“索引是否匹配查询结构”。
用对复合索引:覆盖 WHERE、GROUP BY 和 SELECT 字段
索引列顺序必须严格匹配 GROUP BY 的字段顺序,并优先包含 WHERE 条件列。例如:
- 查询是 WHERE status = 'active' GROUP BY dept, role,那就建 (status, dept, role) 复合索引;
- 如果 SELECT 还要返回 AVG(salary),且 salary 不在索引里,就会回表——这时可考虑把 salary 加进索引末尾,形成覆盖索引(但需权衡写入开销);
- 避免只建单列索引,比如 (dept) 或 (role),它们对多列分组几乎无效。
消除 Using temporary 和 Using filesort
这两个提示是性能瓶颈的明确信号。解决方法包括:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 确认执行计划中 type 是 range 或 ref,而不是 ALL;
- 如果 GROUP BY 后还带 ORDER BY,且顺序一致(如都按 dept ASC),索引可复用;若不一致或含 DESC,MySQL 8.0+ 可建降序索引,如 (dept ASC, created_at DESC);
- 不需要结果排序时,显式加上 ORDER BY NULL,能跳过隐式排序逻辑。
区分松散扫描与紧凑扫描,优先触发 Loose Index Scan
松散索引扫描(Loose Index Scan)只读取每个分组的第一条索引记录,效率远高于逐行扫描。它生效需满足:
- 查询无 WHERE 范围条件,或只有等值条件(如 WHERE dept = 'tech');
- GROUP BY 列是索引最左前缀,且未被表达式包裹(如 GROUP BY UPPER(dept) 会失效);
- 聚合函数仅限 MIN()、MAX()、COUNT(*) 等可由索引首尾直接推导的类型。
慎用预聚合,但高频报表场景值得建汇总表
实时 GROUP BY 再快也有上限。对经营类固定口径报表:
- 把每日/每小时聚合结果存入独立汇总表,主键设为分组字段(如 (date, dept));
- 查询时直接查汇总表,避免每次扫描原始流水;
- 注意验证刷新延迟和口径一致性,尤其涉及 AVG、COUNT(DISTINCT) 等不可加指标时不能简单累加。










