没有绝对更快的distinct或group by,快慢取决于索引、查询列数、mysql版本及执行路径;单列有索引时二者常走相同索引扫描,性能无差异;多列需联合索引覆盖,否则易触发临时表或文件排序;业务需“去重并取最新”时应使用窗口函数而非盲目套用二者。

没有绝对“更快”的那个,快慢取决于你有没有索引、查几列、MySQL 版本,以及优化器实际怎么执行——DISTINCT 和 GROUP BY 走的是两条不同的执行路径,不能靠语法直觉比快慢。
查单列且有索引时,两者常走同一执行计划
比如 SELECT DISTINCT user_id FROM orders 和 SELECT user_id FROM orders GROUP BY user_id,当 user_id 上有 B+Tree 索引,且查询不涉及其他字段时,MySQL 8.0+ 很可能生成完全相同的 EXPLAIN 输出:type: index、Extra: Using index。这时性能基本没差别。
- 别只看语句“长得像”,要跑
EXPLAIN FORMAT=TREE确认是否真用了索引扫描 -
key_len必须匹配索引定义长度(例如联合索引(a, b)上查a,key_len应为 a 字段长度) - 如果
rows接近表总行数,说明索引没生效,实际在全表扫描
多列去重时,索引覆盖要求更严,GROUP BY 可能略占优
查 SELECT DISTINCT user_id, status 或 GROUP BY user_id, status,必须有联合索引 (user_id, status) 才可能避免临时表;单列索引 user_id 或 status 都无效。
- 有覆盖索引时,
GROUP BY更容易触发Using index for group-by,而DISTINCT在部分旧版本或嵌套子查询中可能退化为哈希 + 临时表 - 无索引时,
DISTINCT多走内存哈希去重,GROUP BY更倾向排序分组,容易触发Using filesort和磁盘临时表 - 用
SHOW STATUS LIKE 'Handler_read%'查Handler_read_rnd_next是否飙升,比光看EXPLAIN更准
业务要“去重并留最新一条”,别硬套 DISTINCT 或 GROUP BY
比如“每个用户只取最新一笔订单”,写 SELECT DISTINCT user_id, MAX(order_time) ... 是错的——DISTINCT 不支持聚合混用;写 SELECT user_id, order_time FROM orders GROUP BY user_id 更危险:MySQL 默认返回每组第一条,但这条记录的 order_time 不一定是最大的。
- 真要按规则选行,得用窗口函数:
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC) - 或者子查询 + 关联:
WHERE (user_id, order_time) IN (SELECT user_id, MAX(order_time) ...) - ORM 如 EF Core 把
.Distinct()翻译成GROUP BY时,若字段无索引,性能会断崖下跌,但错误日志里根本看不出问题
真正卡住性能的,往往不是关键字选 DISTINCT 还是 GROUP BY,而是你没确认索引是否被命中、没看 Handler_read_* 状态变量、也没验证过 ORDER BY NULL 是否真让优化器跳过了排序——这些细节比语法选择重要得多。










