select * from view 会全量加载lob字段至内存,因数据库在结果集构建阶段默认读取完整text/clob/blob内容,而非按需截断;sql server、mysql、postgresql均存在此行为,join多表或order by+limit时内存压力倍增。

SELECT * FROM view 为什么会把整个 LOB 加进内存?
不是视图本身有问题,而是数据库执行 SELECT * 时对 LOB 字段的默认加载策略:SQL Server、MySQL 5.7/8.0、PostgreSQL 都会在结果集构建阶段全量读取 TEXT/CLOB/BLOB 内容,哪怕你只显示前 100 字符。尤其当视图底层 JOIN 多张含 LOB 的表时,内存占用是各 LOB 字段体积之和,极易触发 OOM。
- SQL Server 中
text/ntext已弃用,但varchar(max)在未显式截断时仍会全加载;READTEXT或CONVERT(VARCHAR(500), content)才能绕过 - MySQL 8.0+ 的
LONGTEXT使用动态行格式,但优化器无法下推SUBSTRING()到扫描层,WHERE里用SUBSTRING(content, 1, 500) LIKE '%xxx%'也没用 - PostgreSQL 的 TOAST 机制虽压缩存储,但
EXPLAIN ANALYZE中若出现高Heap Fetches,说明频繁回表读取 toast 行,shared_buffers 压力来自缓存未命中
视图定义里含 LOB,ORDER BY + LIMIT 为什么更危险?
你以为 LIMIT 10 能保命,实际数据库很可能先全量加载所有匹配行的 LOB 数据,再放进 sort buffer 排序,最后才截断——排序过程本身就要把每个 LOB 完整读入内存。MySQL 5.7+ 对含窗口函数或 GROUP BY 的视图不支持 ORDER BY LIMIT 下推,EXPLAIN 里看到 sel 就是信号。
- Oracle 12c+ 物化视图含 LOB 时,
REFRESH FAST必然失败,报ORA-22992,因为 MLOG$ 无法记录 LOB 内容 diff,只能存 locator,而 locator 不跨事务复用 - PostgreSQL 中,
ORDER BY blob_col会强制解压并排序整个 BLOB,应改用摘要字段(如md5(blob_col))替代 - SQL Server 若启用
text in row,小 LOB(≤256 字节)存在数据页内,但大 LOB 仍走 LOB 存储链,TOP 10不减少链遍历开销
如何在应用层切断 LOB 全量加载路径?
关键不是改视图,而是控制字段内容长度和加载时机。必须让数据库在物理扫描层就丢弃完整 LOB,而不是传到应用层再裁剪。
- 永远不用
SELECT *:明确列出非 LOB 字段,LOB 字段用SUBSTRING(description, 1, 200)或LEFT(note, 500) - MyBatis 中配
<result column="content" property="content" jdbctype="LONGVARCHAR"></result>+fetchSize="-2147483648"(即 STREAM 模式),驱动按需拉取 - PostgreSQL 用
pg_column_size(blob_col) 在 WHERE 中过滤超大值,避免扫描拖垮 shared_buffers - Oracle 中对 CLOB 模糊匹配必须用
DBMS_LOB.INSTR(dc.body,'keyword') > 0,不能写dc.body LIKE '%keyword%'(只查前 4000 字符且无法走索引)
物化视图或嵌套查询中引用 LOB 字段的典型陷阱
子查询里直接用 LOB 字段做条件判断(比如 IN、JOIN ON、WHERE LIKE),数据库会全量加载 LOB 内容参与计算,而非只读元数据。这比主查询更隐蔽,因为错误常出现在“看似无关”的子句里。
- 用
EXISTS替代IN:把 LOB 过滤逻辑压进子查询内部,外层只收布尔结果,不传 LOB 值 - Oracle 中
DBMS_LOB.COPY批量同步 LOB 时,目标列必须与源列同为SECUREFILE,否则刷新静默截断无警告 - MySQL 嵌套查询中
SELECT clob_col FROM (...) AS t即使外层没用到该列,也会强制加载——必须从子查询里彻底剔除 LOB 字段 - 物化视图所在用户对源表 LOB 列的
SELECT权限必须是直接授予,不能来自角色,否则 REFRESH 查不到 locator
真正卡住的从来不是 LOB 本身,而是你没意识到数据库在哪个环节偷偷把它全搬进了内存。字段级懒加载、查询层长度约束、子查询逻辑下沉——这些动作必须落在 SQL 编写和 ORM 配置的第一线,而不是等 OOM 报错后再去翻日志。











