mysql中SELECT语句中DISTINCT的优化方法

来源:这里教程网 时间:2026-02-28 20:38:48 作者:

为什么 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 + 全字段比对”——索引是否覆盖、字段是否可比较、结果集是否被业务逻辑悄悄撑大,这些细节比记住语法更重要。

相关推荐