加索引仍慢主因是索引未覆盖where+group by+select全链路:必须将where字段置最左(如(status, user_id)),顺序须与group by完全一致,且select中的非分组字段(如amount)需纳入索引以避免回表;函数、隐式转换、错误的having用法等均会导致索引失效。

分组查询卡顿,90% 是因为索引没覆盖 WHERE + GROUP BY + SELECT 全链路,而不是 GROUP BY 本身慢。
为什么加了索引还是慢?
常见错误是只给 GROUP BY 字段单独建索引,比如对 user_id 建单列索引,但查询里还有 WHERE status = 'paid'。这时 MySQL 得先扫一遍 status 索引(或全表),再回主键捞 user_id,最后才分组——等于白建。
- 必须把
WHERE条件字段放索引最左,例如status是过滤条件,就该是(status, user_id) - 如果
SELECT还带SUM(amount),索引得补上amount:即(status, user_id, amount),否则仍要回表 - 索引字段顺序必须和 SQL 中
GROUP BY子句完全一致:GROUP BY dept_id, region→ 索引必须是(dept_id, region),反过来无效 - 用
EXPLAIN看Extra字段:出现Using temporary; Using filesort就说明索引没起作用
哪些写法会让索引彻底失效?
哪怕索引建得再完美,只要 WHERE 或 GROUP BY 里用了这些操作,索引立马退化为全表扫描。
-
WHERE DATE(create_time) = '2024-01-01'→ 改成WHERE create_time >= '2024-01-01' AND create_time -
GROUP BY UPPER(name)→ 提前维护name_upper字段并建索引,或用生成列(MySQL 5.7+):ALTER TABLE t ADD name_upper VARCHAR(100) AS (UPPER(name)); CREATE INDEX idx_name_up ON t(name_upper); -
WHERE user_id = '123'(user_id是BIGINT)→ 字符串参数触发隐式转换,改用数字123 -
GROUP BY YEAR(create_time), MONTH(create_time)→ 改用日期范围 + 预计算列,或直接分区表按月切分
数据量太大时,光靠索引不够怎么办?
当单表超千万行、且分组维度固定(如按天/按地区/按品类统计),索引只能缓解,不能根治。这时候得换思路。
- 高频查询优先走预聚合:建日粒度汇总表
sales_daily(dt, region, category, total_amt, order_cnt),用定时任务跑INSERT ... SELECT ... GROUP BY刷新,查时直接SELECT * FROM sales_daily WHERE dt = '2024-05-06' - PostgreSQL 可用物化视图:
CREATE MATERIALIZED VIEW mv_sales_daily AS SELECT ... GROUP BY ...,配合REFRESH MATERIALIZED VIEW - MySQL 没原生物化视图,可用应用双写、触发器或定时同步保障一致性
- 避免在
HAVING里做本该在WHERE完成的过滤,例如HAVING SUM(amount) > 1000是合理场景,但HAVING region = 'sh'应挪到WHERE前置
真正容易被忽略的是:索引字段顺序错一位、WHERE 参数类型不匹配、或者误以为 COUNT(*) 和 COUNT(col) 性能一样——后者若 col 允许 NULL,且没加 WHERE col IS NOT NULL,就会多一次空值判断,大表上就是几百毫秒差距。











