根本原因是数据库执行路径失控:mysql强制物化含聚合/子查询的视图,postgresql无法下推外层limit到带order by的视图内部,导致全量加载排序或分组。

根本原因不是视图本身,而是数据库执行路径失控:MySQL 强制物化含聚合/子查询的视图,PostgreSQL 无法下推外层 LIMIT 到带 ORDER BY 的视图内部,导致几百万行全量加载进内存再排序或分组。
MySQL 视图一用 GROUP BY 就触发 DERIVED 物化
只要视图定义里出现 GROUP BY、DISTINCT、UNION 或非相关子查询,MySQL 就放弃 MERGE 优化,改走 DERIVED 路径——整个中间结果集先生成临时表,再处理。哪怕你只查 LIMIT 10,它也得把底层几百万行全读进内存排一遍。
-
EXPLAIN SELECT * FROM my_view LIMIT 10中select_type = DERIVED且rows接近底层表总行数,就是坐实物化 -
SHOW STATUS LIKE 'Created_tmp_disk_tables'持续上涨,且与Created_tmp_tables比值 >15%,说明临时表已落盘 - 别信“调大
sort_buffer_size就能解决”——它不控制物化过程,只管排序阶段;物化本身吃的是临时表空间和主内存
PostgreSQL 视图里的 ORDER BY 不会自动下推 LIMIT
视图定义里写了 ORDER BY created_at DESC,外层再加 LIMIT 10,PostgreSQL 优化器大概率不合并这两个操作。结果就是:全量排序 → 再截断 → 内存吃满。尤其当视图含 CTE、LATERAL 或嵌套子查询时,下推基本失效。
- 验证是否下推:
EXPLAIN (VERBOSE, ANALYZE) SELECT * FROM my_view LIMIT 10,看Plan Rows是接近总行数(没下推),还是明显变小(已下推) -
ORDER BY字段没索引?那work_mem调再大也没用——Using filesort+ 全表扫描 = 必爆 - 安全做法:删掉视图里的
ORDER BY,把排序移到最外层,并确保字段(如id)有索引
JOIN 多张大表时,物化 + 缺索引 = 双重内存暴击
视图里写 SELECT * FROM t1 JOIN t2 JOIN t3,一旦其中某张表没走索引(EXPLAIN 显示 type = ALL),数据库就会试图把右表全载入内存建哈希表。此时调 join_buffer_size(MySQL)或 work_mem(PG)风险极高:单次撑住,高并发一来立刻全局 OOM。
- 不要在视图里用
SELECT *,尤其避开TEXT、BLOB字段——它们会让临时表直接落盘,磁盘 IO + 内存双杀 - 为所有
JOIN、WHERE、ORDER BY字段建联合索引,例如CREATE INDEX idx_status_time ON orders (status, created_at) - 手动重写比依赖视图更可控:
SELECT * FROM (SELECT ... FROM t1 JOIN t2) AS v ORDER BY v.id DESC LIMIT 10,显式控制排序和截断位置
真正难处理的,是那些没报错但悄悄 spill 到磁盘的查询——它们不会触发 OOM,只会让整个实例变慢,且很难被常规监控捕获。











