DISTINCT会绕过索引,因其本质是全局去重操作,需遍历所有行判断唯一性,而索引仅加速查找、不加速全局去重,故优化器常选择全表扫描+哈希去重而非利用索引。

视图里加 DISTINCT 本身不直接导致索引失效,但会让优化器放弃使用索引进行快速定位——最终大概率走全表扫描。
为什么 DISTINCT 会绕过索引
数据库执行 DISTINCT 时,本质是去重,需要确认“所有行中该字段值是否唯一”。哪怕目标字段上有索引,优化器也常发现:不如直接扫一遍数据、边读边哈希去重,比反复回表+排序+合并更省事。
- 索引只加速“查找”,不加速“全局去重”;
DISTINCT是聚合类操作,触发的是Using temporary; Using filesort流程 - 如果视图底层查询还带
JOIN、WHERE条件弱(比如没走索引),那DISTINCT更会放大全表扫描代价 - MySQL 8.0+ 对简单单表
DISTINCT有优化(如用松散索引扫描),但前提是字段在索引最左列且无其他干扰条件——实际场景极少满足
DISTINCT 在视图定义里的特殊风险
视图是预定义的 SELECT 语句,一旦定义中含 DISTINCT,所有基于该视图的查询都会继承这个行为,且无法在外部再“取消”它。
- 即使你后续加了
WHERE条件,只要视图定义里有DISTINCT,优化器仍倾向先完成去重再过滤,顺序颠倒 - EXPLAIN 看执行计划时,
type很可能为ALL或index,key字段为空或非预期索引 - 如果视图跨多表
JOIN后DISTINCT,还会隐式触发临时表,进一步拖慢响应
替代方案:什么时候该删掉 DISTINCT,什么时候该换写法
别一上来就给视图加 DISTINCT。先确认业务是否真需要它——很多所谓“去重”其实是 JOIN 导致的假重复。
- 先检查底层表关联逻辑:是不是
LEFT JOIN多对一导致主表记录被撑开?改用EXISTS或子查询通常更高效 - 如果必须去重,优先把
DISTINCT下推到最内层子查询,并确保该子查询能走索引(例如SELECT DISTINCT user_id FROM orders WHERE status = 'paid',且status有索引) - 高频去重字段可建覆盖索引,如
CREATE INDEX idx_status_uid ON orders(status, user_id),让DISTINCT只扫索引不回表 - 实在绕不开,考虑物化结果:用定时任务把去重结果写入汇总表,查视图就变成查普通带索引的表
真正容易被忽略的是:视图里的 DISTINCT 像个“静默放大器”,它不报错、不警告,但每次调用都在悄悄消耗 I/O 和内存。上线前务必用 EXPLAIN FORMAT=TREE 看清它到底扫了什么。











