
为什么 DISTINCT 会创建临时表
MySQL 执行 DISTINCT 时,若无法利用索引的有序性跳过重复值,就会在内存或磁盘中构建临时表(Using temporary),再对整行做哈希或排序去重。这在大表、多列、无索引场景下非常常见。典型表现是 EXPLAIN 输出中 Extra 字段出现 Using temporary; Using filesort。
联合索引必须按查询字段顺序建立
单列 DISTINCT col1 可用 INDEX(col1);但多列如 SELECT DISTINCT col1, col2 FROM t,必须建 INDEX(col1, col2),不能反过来。原因在于:MySQL 只能利用索引最左前缀匹配 WHERE 条件,同时依赖索引项天然有序的特性来“流式跳重”——即遍历索引时,只要下一个 (col1, col2) 组合和上一个不同,就直接取值,无需缓存整行。
错误示例:INDEX(col2, col1) 对 DISTINCT col1, col2 几乎无效带 WHERE 时更关键:比如 SELECT DISTINCT col1, col2 FROM t WHERE col3 = ‘x’,理想索引是 INDEX(col3, col1, col2)如果 col1 选择性极低(如只有 3 个值),即使建了索引,优化器也可能放弃使用
覆盖索引让 DISTINCT 完全不回表
当索引包含 SELECT 中所有字段,且这些字段又参与去重逻辑时,MySQL 就能纯靠索引页完成整个操作,彻底避免访问数据行(即“回表”)。这是性能飞跃的关键。
例如:表有 id, status, user_id, create_time,查询 SELECT DISTINCT user_id FROM t WHERE status = 1,建 INDEX(status, user_id) 即可覆盖别加多余字段:INDEX(status, user_id, create_time) 没坏处,但 create_time 不在 SELECT 列表里,就是冗余,还增大索引体积NULL 值不影响索引结构,但大量 NULL 会降低选择性,必要时可加 NOT NULL 约束
GROUP BY 在索引友好度上往往更可控
虽然 DISTINCT 和 GROUP BY 单列去重结果一致,但 MySQL 对 GROUP BY 的索引优化路径更成熟,尤其支持松散索引扫描(Loose Index Scan)——即只扫索引中每个分组的第一条记录,跳过中间重复项。而 DISTINCT 是否启用该优化,高度依赖版本和索引是否“严丝合缝”。
测试建议:对同一语义写两版,都跑 EXPLAIN,看谁没 Using temporary示例对比:SELECT DISTINCT a, b FROM t vs SELECT a, b FROM t GROUP BY a, b如果后续要加 COUNT(*) 或 HAVING,直接用 GROUP BY 更自然,不用改结构
真正卡住性能的,往往不是 DISTINCT 本身,而是它暴露了索引缺失或设计偏差。每次看到 Using temporary,先检查 WHERE 条件字段 + SELECT 字段是否被一个联合索引完整覆盖,而不是急着调大 sort_buffer_size。

评论(0)