DISTINCT显著拖慢查询,因其本质是隐式GROUP BY,需全量拉取字段建哈希表或排序去重,易触发临时表、磁盘IO及谓词下推失效;仅当SELECT字段严格匹配联合索引最左前缀且覆盖全部列、WHERE条件亦命中该索引时才可能走索引。

SELECT DISTINCT 会显著拖慢查询,因为它不是简单过滤,而是强制数据库启动一套完整的去重流程——本质是隐式 GROUP BY,必须把所有 SELECT 字段的值全拉出来,建哈希表或排序,再逐行比对。
为什么 DISTINCT 不是“加个关键字就完事”
DISTINCT 对整行结果做唯一性判断,不是对单列“筛一遍”。只要任意一个字段值不同,就算新行。这意味着:
- 数据库无法跳过重复区间,哪怕索引有序,也常被迫走 Tight Index Scan(紧凑扫描),而非 Loose Index Scan(松散扫描)
- 若字段含
TEXT、BLOB或长VARCHAR,哈希表内存暴涨,tmp_table_size很快被撑爆,触发磁盘临时表(Created_tmp_disk_tables上升) -
EXPLAIN中高频出现Using temporary和Using filesort,就是它在后台建临时结构的铁证
索引对 DISTINCT 基本没用,除非满足三个硬条件
别指望给单列加索引就能提速。能走索引的前提非常苛刻:
- 必须建联合索引,且列顺序与
DISTINCT字段顺序严格一致 ——DISTINCT status, user_id无法利用(user_id, status)索引 - 该联合索引必须覆盖全部
SELECT字段(即“覆盖索引”),否则仍要回表,去重前就得先读聚集索引 -
WHERE条件也得命中这个索引的最左前缀,否则索引直接失效;比如WHERE created_at > '2024-01-01'却只建了(user_id, status),照样全表扫
GROUP BY 替代 DISTINCT 并不总是更优,但可控性更强
语义等价时,GROUP BY 常比 DISTINCT 更容易触发松散索引扫描,尤其当后续可能加聚合函数(如 MIN(updated_at))时:
-
SELECT city, district FROM users GROUP BY city, district在有(city, district)索引时,可能跳过重复块;而SELECT DISTINCT city, district FROM users有时仍会逐行比对 - 但注意:混用
GROUP BY和DISTINCT是逻辑错误,如SELECT DISTINCT dept_id, COUNT(*) FROM t GROUP BY dept_id——GROUP BY已保证dept_id唯一,DISTINCT纯属冗余,反而触发双重临时表 - MySQL 8.0+ 对单表
COUNT(DISTINCT)启用哈希聚合,但一旦 JOIN 或嵌套子查询,立刻退化回排序路径
真正卡住性能的,往往不是语法本身,而是字段组合和中间结果膨胀
最容易被忽略的是业务逻辑悄悄放大了去重压力:比如 SELECT DISTINCT CONCAT(name, '-', id), email,表达式结果无法走索引,必须全表计算后再比对;又比如视图定义里写了 SELECT DISTINCT a, b FROM t JOIN s,调用时加 WHERE a = 'x',MySQL 仍会先物化整个 JOIN 结果,再过滤——谓词下推彻底失效。DISTINCT 的代价,永远藏在“你没意识到它正在比对什么”的地方。

















