DISTINCT 变慢是因为其本质是隐式 GROUP BY,会触发临时表和文件排序;需用匹配顺序的联合覆盖索引优化,或改写为 GROUP BY 启用松散索引扫描。
MySQL 的 DISTINCT 本质是去重,执行时通常会触发临时表(Using temporary)和文件排序(Using filesort),尤其当字段没索引、结果集大或涉及多列组合时,性能下降明显。它不是语法糖,而是隐式 GROUP BY,MySQL 5.7+
后底层基本等价于 GROUP BY 字段列表。
对 SELECT DISTINCT col1, col2 加单列索引 (col1) 或 (col2) 通常无效;必须建联合索引且满足最左前缀原则,并尽可能覆盖查询字段:
ALTER TABLE t ADD INDEX idx_distinct (col1, col2) —— 能让 MySQL 直接遍历索引树去重,避免回表和临时表col3(如 SELECT DISTINCT col1, col2, col3),单靠 (col1, col2) 索引仍要回表,此时应考虑覆盖索引:(col1, col2, col3)
DISTINCT 字段顺序一致,DISTINCT col2, col1 无法利用 (col1, col2) 索引在某些场景下,显式写 GROUP BY 比 DISTINCT 更容易触发优化器的松散索引扫描(Loose Index Scan),大幅减少扫描行数。前提是:分组字段有合适索引,且无聚合函数或 HAVING 子句干扰。
SELECT DISTINCT city, district FROM users; -- 可尝试改写为: SELECT city, district FROM users GROUP BY city, district;
两者语义等价,但后者在有 (city, district) 索引时,可能跳过重复值区间,而前者有时仍走紧凑扫描(Tight Index Scan)。
DISTINCT 对所有选中列做哈希/排序比对,若包含 TEXT、BLOB 或长 VARCHAR,不仅内存占用高,还可能触发磁盘临时表(Created_tmp_disk_tables 上升)。常见错误包括:
SELECT DISTINCT *, created_at FROM logs —— * 让优化器无法裁剪,且大字段拖慢整个去重流程SELECT DISTINCT CONCAT(name, '-', id) AS key FROM t —— 表达式结果无法走索引,强制全表计算再去重真正卡顿的地方往往不在语法本身,而在没意识到 DISTINCT 是“隐式 GROUP BY + 全字段比对”——索引是否覆盖、字段是否可比较、结果集是否被业务逻辑悄悄撑大,这些细节比记住语法更重要。