为什么 DISTINCT 会变慢
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 常能启用松散索引扫描
在某些场景下,显式写
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)。
避免 SELECT * + DISTINCT 和大字段参与去重
DISTINCT对所有选中列做哈希/排序比对,若包含
TEXT、
BLOB或长
VARCHAR,不仅内存占用高,还可能触发磁盘临时表(
Created_tmp_disk_tables上升)。常见错误包括:
SELECT DISTINCT *, created_at FROM logs——
*让优化器无法裁剪,且大字段拖慢整个去重流程
SELECT DISTINCT CONCAT(name, '-', id) AS key FROM t—— 表达式结果无法走索引,强制全表计算再去重 正确做法:只选必要字段;用主键或唯一标识替代长文本拼接;必要时先子查询过滤再 DISTINCT
真正卡顿的地方往往不在语法本身,而在没意识到 DISTINCT 是“隐式 GROUP BY + 全字段比对”——索引是否覆盖、字段是否可比较、结果集是否被业务逻辑悄悄撑大,这些细节比记住语法更重要。
