DISTINCT在大数据量下变慢是因为需全量取数后排序去重,导致IO和内存压力大;GROUP BY可利用索引避免排序,但仅当分组列具业务唯一性时才安全替代。

为什么 DISTINCT 在大数据量下容易变慢
DISTINCT 本质是先全量取出结果,再做去重(通常走临时表 + 排序),数据一多,磁盘 IO 和内存压力就上来了。尤其当查询字段多、有大文本或 JSON 字段时,临时表膨胀得特别快。
而 GROUP BY 在多数情况下能复用索引(尤其是联合索引的最左前缀匹配),避免排序,执行计划里常显示 Using index;MySQL 8.0+ 还支持松散索引扫描(Loose Index Scan)优化 GROUP BY,进一步跳过重复值扫描。
但注意:两者语义不完全等价 —— DISTINCT 是对整行去重,GROUP BY 是按指定列分组,若没加聚合函数,MySQL 默认取每组第一行(行为依赖 sql_mode,5.7+ 默认开启 ONLY_FULL_GROUP_BY,会报错)。
怎么安全地用 GROUP BY 替代 DISTINCT
核心原则:只在「你明确知道要按哪些列去重」且「这些列能构成业务唯一性标识」时替换。比如查所有不重复的 user_id,而不是「所有字段都不重复的完整记录」。
- 如果原语句是
SELECT DISTINCT user_id FROM orders,直接改SELECT user_id FROM orders GROUP BY user_id即可 - 如果涉及多列去重,如
SELECT DISTINCT city, category FROM products,对应改成SELECT city, category FROM products GROUP BY city, category - 如果原查询带
WHERE条件或JOIN,确保GROUP BY的列都在WHERE过滤后的小结果集上,否则优化无效 - MySQL 5.7+ 启用
ONLY_FULL_GROUP_BY时,不能省略非分组列;若必须返回其他字段(如最新一条记录的created_at),得显式加聚合函数:MAX(created_at)或ANY_VALUE(name)
GROUP BY 可能比 DISTINCT 更慢的几种情况
不是所有场景都适合硬换。以下情况反而可能更差:
- 分组字段无索引,且数据分布极不均匀(比如 99% 记录
status = 'pending'),GROUP BY仍要扫全表建哈希表 - 使用了
ORDER BY且排序字段不在GROUP BY列中,MySQL 无法利用索引排序,会额外触发Using filesort - MySQL 版本低于 5.7,
GROUP BY默认强制排序,性能反而不如DISTINCT - 查询含
LIMIT且去重后结果极少(比如查前 10 个不重复tag),DISTINCT可能提前终止,而GROUP BY往往要跑完全部分组
验证是否真优化了:看 EXPLAIN 和实际耗时
别只信“理论上更快”。执行前后都跑一遍 EXPLAIN FORMAT=TRADITIONAL,重点关注:
-
type是否从ALL变成range或ref -
Extra里有没有Using temporary(越少越好)、Using filesort(最好没有) -
rows扫描行数是否显著下降 - 用
BENCHMARK()或多次SELECT SQL_NO_CACHE ...测真实耗时,注意清空查询缓存(RESET QUERY CACHE或重启连接)
真正卡住的,往往不是语法替换本身,而是分组字段没索引、或者误把「逻辑去重」当成「物理去重」来处理 —— 比如用 GROUP BY id 代替 DISTINCT *,结果只是把每行自己分了一组,毫无意义。


















