distinct 和 group by 性能差异取决于索引、mysql 版本及优化器行为:无索引时 distinct 多用哈希去重,group by 易触发 filesort;有覆盖索引时 group by 更易利用索引顺序,而 distinct 优化更保守;多列去重需联合索引覆盖才高效;真正瓶颈常由 handlerread* 状态变量暴露。

单纯比“快慢”没意义——DISTINCT 和 GROUP BY 的执行路径根本不同,性能差异取决于你有没有索引、查几列、用什么 MySQL 版本,以及优化器是否真把它当一回事。
MySQL 8.0+ 之前,DISTINCT 常默认触发 filesort
无索引时,DISTINCT 多数走哈希去重(内存中建 hash 表,边读边判重),不强制排序;而老版本 GROUP BY 默认按分组字段排序,哪怕加了 ORDER BY NULL,优化器也常忽略。这就导致 GROUP BY 额外多一次 Using filesort,尤其在百万行以上表里,I/O 和 CPU 开销明显更高。
- 验证方法:对同一语句跑
EXPLAIN,重点看Extra字段是否含Using filesort或Using temporary - 典型陷阱:ORM(如 EF Core)把
.Distinct()翻译成GROUP BY,结果在无索引字段上查得极慢,却以为是“语法问题”
有覆盖索引时,GROUP BY 更容易跳过排序
当去重字段上有 B+Tree 索引,且查询只涉及该索引列(或最左前缀),GROUP BY 可直接利用索引顺序输出,Extra 显示 Using index for group-by;而 DISTINCT 在某些旧版本或嵌套子查询中,未必能识别这种优化路径,仍可能回表或建临时表。
- 例如联合索引
(user_id, status),SELECT user_id FROM t GROUP BY user_id很可能走索引扫描;但SELECT DISTINCT user_id FROM t不一定 - MySQL 8.0 启用
group_by_optimization后,部分简单GROUP BY会被自动重写为DISTINCT,但不能依赖——得看EXPLAIN FORMAT=TREE输出里有没有grouping_operation
多列去重时,索引覆盖要求更苛刻
DISTINCT a, b 和 GROUP BY a, b 语义等价,但性能表现差异放大:两者都要求索引覆盖 (a, b) 才高效;否则都要建临时表 + 排序/哈希。但 GROUP BY 在部分场景下更“激进”地尝试索引合并,而 DISTINCT 更保守。
- 常见误判:以为加了单列索引
a就够,实际DISTINCT a, b仍要全表扫描 - 执行计划里
type: index不代表快——要看key_len是否匹配联合索引长度,以及rows是否接近表总行数
真正卡住性能的,往往不是关键字选哪个,而是你没看 Handler_read_next 或 Handler_read_rnd_next 这类状态变量——它们暴露了是否真的在走索引,还是在磁盘上随机翻页。别只盯着 EXPLAIN 的 Extra 字段就下结论。











