不建议将text字段放在主表中,因其采用外部存储导致额外i/o、整行重写和缓存效率下降;参与查询易引发临时表和oom;放大binlog体积并加剧主从延迟;安全做法是拆至冷表、限制使用场景并避免select*。

TEXT 字段不建议放在主表里,不是因为它“不能存”,而是它会悄无声息地拖垮整张表的性能和稳定性。真正在生产环境踩过坑的人,基本都经历过 INSERT 变慢、SELECT * 卡顿、ORDER BY 直接超时、备份失败这些现象。
为什么 TEXT 会让主表变“胖”且难维护?
TEXT 字段在 InnoDB 中默认采用“外部存储”:实际内容不和行数据一起存,而是在单独的页(off-page)里保存,行内只留一个 20 字节的指针。这带来三个直接后果:
- 每次读取该字段都要额外一次或多次磁盘 I/O(哪怕只查 id 和 status,只要语句没明确排除 TEXT 字段,InnoDB 仍可能加载它)
- UPDATE 任意字段时,如果该行包含 TEXT,InnoDB 往往触发整行重写(row relocation),引发 page split 和碎片
- 表的平均行长度虚高,影响 buffer pool 缓存效率,innodb_buffer_pool_size 实际命中率下降
TEXT 参与查询时为什么容易雪崩?
只要TEXT 字段出现在以下任一场景,性能风险就急剧上升:
- 出现在 WHERE 条件中(哪怕只是 log IS NOT NULL)
- 出现在 ORDER BY、GROUP BY 或 JOIN 的关联条件里
- 被用于 LIKE '%keyword%'(前缀模糊搜索无法用前缀索引加速)
- 和其他字段一起出现在 SELECT * 或未限定字段的查询中
InnoDB 会为这类操作临时把整个 TEXT 内容加载进 sort_buffer 或 tmp_table,极易触发磁盘临时表(Created_tmp_disk_tables 指标飙升),甚至 OOM。
binlog 和复制链路为什么会被 TEXT 拖垮?
TEXT 字段会显著放大 binlog 体积和写入压力:
- 每次 INSERT/UPDATE 都要把完整内容写入 binlog(即使只改了 status 字段)
- max_binlog_size 被快速填满,导致频繁切换 binlog 文件
- 主从复制延迟直线上升,尤其在大字段 + 高频写入场景(比如日志表)
- 如果开启 binlog_row_image = FULL(默认),旧值 + 新值全记,体积翻倍
真正安全的 TEXT 使用姿势是什么?
不是禁用,而是隔离和约束: - 把TEXT 字段单独拆到一张“冷表”,用 main_id 关联,主表只留 ID 和摘要字段
- 冷表加 ROW_FORMAT=COMPRESSED + KEY_BLOCK_SIZE=8(适用于 5.7+)
- 禁止在该字段上建任何非全文索引;如需搜索,走 Elasticsearch 或预处理关键词落库
- 写入前做长度校验,超长内容截断或转存对象存储(OSS/S3),数据库只存 URL
- 应用层明确指定需要的字段,永远不用 SELECT * 查含 TEXT 的表
最常被忽略的一点:即使你从不查 TEXT 字段,只要它在表结构里,InnoDB 就会在某些优化路径(比如覆盖索引判断)中多做一次元数据检查——这点开销单次不明显,但在 QPS 上万的接口里,就是压垮骆驼的最后一根稻草。











