distinct用临时表还是索引取决于字段是否有合适索引:有索引(尤其是联合索引且顺序匹配)则走索引去重,避免临时表;无索引则全表扫描并使用临时表+排序/哈希去重,explain中显示using temporary即为此路径。

DISTINCT 用的是临时表还是索引,取决于字段有没有索引
MySQL 不会固定用某一种方式执行 DISTINCT。它先看查询字段是否有可用索引,再决定走哪条路径:有索引就走索引扫描去重,没索引就建临时表+排序/哈希去重。
典型现象是执行 EXPLAIN 后看到 Extra 列出现 Using temporary —— 这说明 MySQL 正在用磁盘或内存临时表存所有结果,再逐行比对去重。2000 万行数据全扫一遍,还写临时表,慢是必然的。
而如果字段上有索引(比如 CREATE INDEX idx_user_id ON orders(user_id)),EXPLAIN 就不会显示 Using temporary,type 可能变成 index,Extra 是空或 Using index。这时 MySQL 直接遍历 B+ 树叶子节点,相同值天然聚在一起,跳过重复即可,完全不碰临时表。
为什么 DISTINCT 多列时容易掉进坑里
多列 DISTINCT(如 SELECT DISTINCT a, b FROM t)要求组合值唯一,但 MySQL 只能利用「最左前缀匹配」的索引。也就是说:
- 只有当存在
(a, b)联合索引时,才可能触发索引去重 - 仅有
a单列索引,或仅有b单列索引,都不行 - 索引顺序必须和
DISTINCT字段顺序一致,(b, a)索引对DISTINCT a, b无效 - 如果语句里还带了
WHERE条件,优化器可能优先选 WHERE 字段的索引,导致DISTINCT字段无法走索引去重
常见错误是以为加了单列索引就万事大吉,结果 EXPLAIN 仍显示 Using temporary,就是栽在这点上。
DISTINCT 和 GROUP BY 在底层真的一样吗
不一定。虽然两者在单列、无其他计算、无 ORDER BY 的简单场景下,优化器常会生成几乎相同的执行计划,但它们的语义和约束不同,导致底层行为可能分化:
-
DISTINCT是结果集过滤操作,不强制要求字段参与逻辑分组;GROUP BY是分组操作,所有非聚合字段必须显式出现在GROUP BY子句中 - MySQL 5.7+ 严格模式下,
SELECT DISTINCT a, b FROM t和SELECT a, b FROM t GROUP BY a, b执行计划往往一致;但一旦加入ORDER BY或LIMIT,优化器可能为GROUP BY强制加Using filesort,而DISTINCT可能省掉这步 - 某些版本中,
GROUP BY会隐式触发loose index scan优化(跳过部分索引节点),而DISTINCT不一定享受同等待遇 - 当字段含大量 NULL 值时,
DISTINCT把所有 NULL 当作相同值只留一个;GROUP BY行为一致,但若配合聚合函数(如COUNT(a)),NULL 就会被忽略——这是语义差异带来的间接影响
别只盯着 DISTINCT 本身,先看执行计划
真正决定快慢的,从来不是关键字写法,而是 EXPLAIN 输出里的这几项:
-
type:要是ALL或index但没走覆盖索引,基本意味着全表/全索引扫描 -
key和key_len:是否命中预期索引?长度是否符合预期(比如联合索引只用到前两列,key_len就不会是三列总长)? -
Extra:出现Using temporary或Using filesort就要警惕;Using index是理想状态 - 注意
rows预估数:如果远大于你心里预期的“去重后结果量”,说明扫描范围过大,索引没生效或选错了
很多人调优时直接改 SQL 写法(比如把 DISTINCT 换成 GROUP BY),却跳过 EXPLAIN 看实际路径,最后发现只是碰巧换写法后让优化器选了另一个索引——问题根子还在索引设计和条件匹配上。











