数据库优化器会为JOIN表中的TEXT字段预分配大内存,导致临时表落盘和性能下降;应采用延迟关联或EXISTS替代JOIN来避免加载大字段。

因为数据库优化器会为每行预分配大字段内存空间,哪怕你根本没选它,也会触发临时表写磁盘、Using temporary 和 Using filesort。
TEXT字段在JOIN阶段就被加载进中间结果集
MySQL(尤其是5.7+)执行JOIN时,只要右表含TEXT、BLOB或超长VARCHAR,优化器就会按最大可能长度(比如几MB)为每一行预留内存。这导致:
- 即使
SELECT里没写remark,只要order表有TEXT字段且参与JOIN,它就进中间结果 -
tmp_table_size很快被撑爆,强制落盘到磁盘临时表 - 大量
Using temporary出现在EXPLAIN的Extra列里 - LEFT JOIN无匹配时,仍为NULL行预留大字段空间,浪费更明显
延迟关联:用ID代替整行JOIN
核心是把“关联”和“取大字段”拆开,避免批量搬运。关键操作有:
- 子查询只选驱动所需的最小列:
order_id、user_name等小字段,不碰remark - 外层再用主键或唯一索引(如
order_id)单点关联原表补字段 - 确保被补查的表上关联字段有索引,否则外层JOIN会退化成全表扫描
- 示例中
base.oid = o2.order_id必须依赖order.order_id是主键或唯一索引
比JOIN更轻量的替代:用EXISTS过滤
当大字段只用于WHERE条件(比如“备注含紧急”),完全没必要JOIN出来——直接跳过搬运:
- 把
JOIN order o ON ... AND o.remark LIKE '%紧急%'改成EXISTS (SELECT 1 FROM order o WHERE o.user_id = u.id AND o.remark LIKE '%紧急%') -
EXISTS只判断存在性,不读取remark内容,IO开销几乎为零 - 如果
remark上有全文索引,还能进一步加速匹配 - 注意:
EXISTS不能替代SELECT需要的大字段值,仅适用于过滤场景
真正卡住的不是TEXT本身,而是它被拖进JOIN执行路径里。只要不让它出现在关联阶段,哪怕字段再大,也不会拖慢整个查询。最容易被忽略的是:即使你写了SELECT *但没显式引用TEXT列,只要它属于JOIN表,就可能被提前加载——得靠EXPLAIN FORMAT=JSON确认used_columns才能看清实际加载了哪些列。

















