distinct慢的根本原因是mysql将其隐式转为group by,触发using temporary和using filesort;需建与distinct字段顺序一致的联合覆盖索引,避免大字段、表达式及全表扫描。

为什么DISTINCT会慢到需要优化
DISTINCT慢,根本原因不是语法本身,而是它在MySQL里被当作隐式 GROUP BY 处理——会触发 Using temporary 和 Using filesort,尤其当字段没索引、结果集大或含长文本时。你看到的“30秒变0.01秒”,往往就卡在这两个 Extra 标志上。
必须建联合覆盖索引,单列索引基本无效
对 SELECT DISTINCT col1, col2 建 (col1) 或 (col2) 单列索引,几乎没用。MySQL 无法利用它们跳过重复值区间。
- 正确做法:建联合索引,且顺序必须和
DISTINCT字段完全一致,比如ALTER TABLE t ADD INDEX idx_c1_c2 (col1, col2) - 如果查询还带了
col3(如SELECT DISTINCT col1, col2, col3),索引要扩展为(col1, col2, col3)才能覆盖,避免回表 - 字段含大量
NULL会影响索引效率,关键去重字段建议加NOT NULL约束
用 GROUP BY 替代 DISTINCT 可能更快
语义等价的 SELECT DISTINCT a,b FROM t 和 SELECT a,b FROM t GROUP BY a,b,执行计划可能完全不同。后者更容易触发松散索引扫描(Loose Index Scan),跳过中间重复块,大幅减少扫描行数。
- 前提:
a,b上有联合索引,且查询不带HAVING或聚合函数干扰 - 验证方式:对两者都跑
EXPLAIN,重点看type是否为index,以及Extra是否还有Using temporary - 注意:
GROUP BY默认不保证顺序,如需排序,仍要显式加ORDER BY
别让大字段和表达式拖垮去重
DISTINCT 是对整行结果做哈希/排序比对。一旦涉及 TEXT、BLOB 或长 VARCHAR,内存不够就会写磁盘临时表,Created_tmp_disk_tables 指标会飙升。
- 避免
SELECT DISTINCT *或拼接表达式,比如SELECT DISTINCT CONCAT(name, '-', id)—— 表达式结果无法走索引,只能全表计算 - 真要对长内容去重?先存哈希值:加
content_md5 CHAR(32)字段,建索引,查DISTINCT content_md5 - 提前用
WHERE过滤,比如WHERE status = 'active',别让 DISTINCT 处理全表 2000 万行
真正容易被忽略的是:DISTINCT 的去重单位永远是“组合行”,不是单个字段;而优化成败,往往取决于索引是否覆盖、顺序是否匹配、以及有没有无意中把大字段拖进 SELECT 列表里。











