根本原因不是“重复值多”,而是视图定义触发低效执行算法:含 DISTINCT/GROUP BY 等时 MySQL 强制 TEMPTABLE 算法,全量物化再过滤,绕过索引;缺联合索引致排序开销暴增;DISTINCT 与多表 JOIN 混用更放大性能问题;且视图不缓存执行计划,需定期 EXPLAIN ANALYZE 监控。

根本原因不是“重复值多”,而是视图定义触发了低效执行算法,让数据库被迫全量扫描+排序去重,而不是利用索引快速聚合。
视图含 DISTINCT 或 GROUP BY 时 MySQL 自动降级为 TEMPTABLE 算法
只要视图定义里出现 DISTINCT、GROUP BY、UNION、标量子查询或窗口函数,MySQL 就无法用 MERGE 算法内联展开,会强制走 TEMPTABLE:先物化整个中间结果集到磁盘临时表,再对外层条件过滤或 JOIN。
- 用
SHOW CREATE VIEW your_view查看,如果返回中明确含ALGORITHM = TEMPTABLE,就确认踩中此坑 - 哪怕你只查
SELECT * FROM your_view WHERE id = 123,MySQL 仍会先算出全部分组/去重结果,再过滤——完全绕过索引 - 临时表默认无索引,后续 JOIN 或 ORDER BY 全部在内存/磁盘上硬扫
重复值多 + 缺少联合索引导致排序开销爆炸
当视图要做 GROUP BY a, b 或 DISTINCT a, b,而底层表没建 (a, b) 联合索引时,数据库只能:
- 全表扫描读取所有行
- 把每行的
a, b值暂存进内存 hash 表或排序缓冲区(sort_buffer_size) - 若数据量超缓冲区,就会写磁盘归并排序——I/O 成为瓶颈
例如:1000 万行用户行为日志,按 user_id, event_type 去重,但只有单列 user_id 索引,没有 (user_id, event_type) 联合索引,DISTINCT 执行时间可能从 200ms 涨到 12s。
多表 JOIN 视图里用 DISTINCT 是性能杀手
视图定义中写 SELECT DISTINCT u.id, u.name, o.status FROM users u LEFT JOIN orders o ON u.id = o.user_id,逻辑陷阱在于:
-
DISTINCT是最后一步操作,数据库必须先完成 LEFT JOIN,生成笛卡尔积级中间结果(一个用户 5 条订单 → 5 行),再逐行比对整行是否重复 - 即使你只需要每个用户的最新订单状态,这个写法也强迫数据库保留并比较全部组合
- 更糟的是,
o.status取哪一行完全不确定——可能是任意一条,业务逻辑已不可控
正确做法是改用窗口函数:ROW_NUMBER() OVER (PARTITION BY u.id ORDER BY o.created_at DESC),然后外层 WHERE rn = 1,这样能下推过滤、走索引、避免冗余计算。
最常被忽略的一点:视图本身不缓存执行计划。每次查询都重新解析、重写、估算统计信息——如果底层表数据分布突变(比如某天突然插入千万条测试数据),优化器可能选错驱动表或放弃索引,而你根本不会收到告警。别只盯着视图定义,得定期用 EXPLAIN ANALYZE 对典型查询做快照比对。

















