视图展开本身不耗内存,真正吃内存的是展开后触发的强制物化、全量排序或lob字段加载;mysql遇group by等强制derived物化,postgresql视图中order by常无法下推limit,导致全量排序,select *读取含text/blob字段时更会因全量加载lob直接压垮临时表内存限制。

视图展开本身不耗内存,真正吃内存的是展开后触发的强制物化、全量排序或LOB字段加载——尤其当底层表新增了TEXT、JSON或BLOB字段,而视图仍用SELECT *时,几百万行+大字段直接压垮temptable_max_ram或work_mem。
MySQL 视图展开后变成 DERIVED 物化怎么办
只要视图定义含GROUP BY、DISTINCT、UNION或子查询,MySQL 就放弃 MERGE 优化,改走 DERIVED 路径:整个中间结果先生成临时表,再处理。哪怕外层加了LIMIT 10,它也得把底层几百万行全读进内存。
- 用
EXPLAIN FORMAT=TREE SELECT * FROM my_view LIMIT 10看materialized_from_subquery: true,或select_type = DERIVED且rows接近底层表总行数,就是坐实物化 - 别调
sort_buffer_size——它只管排序阶段,不控制物化过程;真正该盯的是temptable_max_ram和max_heap_table_size - 会话级先试:
SET SESSION temptable_max_ram = 67108864(64MB),再跑查询,观察SHOW STATUS LIKE 'Created_tmp_disk_tables'是否明显下降 -
max_heap_table_size必须 ≥temptable_max_ram,否则 TempTable 退化成 MyISAM 临时表,更吃内存
PostgreSQL 视图里 ORDER BY 不下推导致全量排序
视图定义写了ORDER BY created_at DESC,外层再加LIMIT 10,PostgreSQL 优化器大概率不合并这两个操作。结果是:全量排序 → 再截断 → 内存爆满。CTE、LATERAL 或窗口函数一出现,下推基本失效。
- 验证是否下推:
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM my_view LIMIT 10,看Plan Rows是否远大于 10 - 删掉视图里的
ORDER BY,把排序移到最外层:SELECT * FROM my_view ORDER BY id DESC LIMIT 10 - 确保
id或created_at有索引;否则即使下推,也会触发External sort写磁盘临时文件 - 避免在视图里用
JSON_EXTRACT(col, '$.name')或UPPER(name)做排序——函数表达式会让索引失效,直接触发全表扫描+filesort
SELECT * + LOB 字段让临时表直接落盘
SELECT *从含TEXT、BLOB的视图查数据,数据库往往不区分“要不要完整加载”,而是默认把整列塞进内存。JOIN 多张表时,多个 LOB 字段叠加,Created_tmp_disk_tables飙升,IO + 内存双杀。
- 永远不用
SELECT *:显式列出业务需要的字段,对 LOB 字段加长度约束,例如SUBSTRING(content, 1, 500)或LEFT(note, 200) - SQL Server 中慎用
ntext/text,应迁至NVARCHAR(MAX)并配合CONVERT(VARCHAR(500), content) - PostgreSQL 中用
pg_column_size(blob_col) 过滤超大值,避免扫描时拖垮<code>shared_buffers - 应用层开启流式读取:MyBatis 配置
fetchSize="-2147483648"(即 STREAM 模式),JDBC 驱动按需拉取而非缓存整列
真正难处理的,不是视图语法有多复杂,而是物化边界模糊 + 排序字段没索引 + LOB 加载不可控——这三者叠在一起,EXPLAIN看起来都正常,但一跑并发就 OOM。










