大字段参与join会触发lob全量加载,导致io、内存和执行计划三重崩坏;应通过投影裁剪、拆表或异步加载使大字段远离join过程。

大字段触发LOB全量加载,JOIN时根本停不下来
SQL Server 和 MySQL 都会把 TEXT、VARCHAR(MAX)、NVARCHAR(MAX)、XML 这类字段当作 LOB(Large Object)单独存储,不在主数据页里。一旦 JOIN 的 ON、WHERE、SELECT 或排序中出现这些列,数据库就不得不把整块 LOB 内容从磁盘读进内存——哪怕你只取 1 行,也可能拉取几 MB 数据。这不是“多传几个字节”的问题,是 IO + 内存 + 执行计划三重崩坏。
- 执行计划里看到
LOB Logical Reads高得离谱,基本就是它在拖后腿 - 即使没显式
SELECT大字段,只要该表是驱动表(比如LEFT JOIN的左表),且统计信息不准,优化器可能误选Nested Loop,导致每行都触发 LOB 定位开销 -
TEXT/NTEXT已弃用,但存量系统还在用;VARCHAR(MAX)看似现代,实际 >8000 字节就自动转为 LOB 存储
SELECT * 或隐式引用大字段,等于主动打开性能黑洞
写 SELECT * 做 JOIN,不只是字段多的问题。数据库必须加载所有列对应的数据页,而大字段会强制加载 LOB 页,撑爆缓冲池、拖慢排序分组、让临时表操作直接卡死。更隐蔽的是:ORM 框架默认 eager-load 全字段,@OneToMany 一配,底层 SQL 就悄悄拖进来整个 content 字段,你根本没意识到。
- MySQL 执行计划出现
Using temporary,大概率就是宽字段 + 全量列撑爆内存 - PostgreSQL 中字段越多,tuple 头部开销越大,尤其在百万级结果集下明显
- 别信“我只用了其中一两个字段”——优化器不会倒推裁剪被驱动表的列,JOIN 前不收敛,就晚了
在 ON/WHERE 里对大字段做函数或模糊匹配,索引彻底失效
哪怕只是写个 LEN(detail_text) > 100 或 detail_text LIKE '%error%',都会让优化器放弃所有索引(包括全文索引,除非你显式用 CONTAINS)。结果就是全表扫描 + 每行加载 LOB,性能断崖式下跌。
- 绝对不要写
ON t1.desc = t2.desc(两表都是NVARCHAR(MAX))——截至 2026 年 6 月,SQL Server 仍无法高效处理这种等值比对 - 模糊查询想提速?必须建全文索引,并改用
CONTAINS或FREETEXT,不能靠LIKE - 函数操作不仅阻止索引使用,还会让统计信息失效,进一步误导优化器选错 JOIN 算法
真正有效的解法:让大字段彻底远离 JOIN 过程
核心不是“怎么更快地读大字段”,而是“让 JOIN 根本不碰它”。把连接键和过滤字段先抽成轻量中间集,再按需回查。这才是可控、可预测、能上生产的方法。
- 用 CTE 或派生表只
SELECT id, ref_id, status等非 LOB 字段,确保 WHERE 条件能走索引 - 再用这个结果集
INNER JOIN原表,只取需要的大字段——此时结果集已大幅缩小,LOB 加载总量可控 - 拆表:把
full_text单独放到article_contents(article_id, full_text)表,主表只留摘要和 ID - 异步加载:应用层先查 JOIN 主干,再按需发第二条 SQL 查大字段,避免阻塞主流程
大字段本身不参与业务逻辑,却最容易在 JOIN 中被无意卷入。最危险的不是“不知道要优化”,而是“以为只 SELECT 了几个字段,其实 LOB 已经全量加载”。










