结论是更新LONGBLOB或LONGTEXT字段本身不是瓶颈,真正卡住的是每次更新都读全量再写全量及off-page存储引发的额外I/O跳转;应避免UPDATE语句触碰大字段,除非确需修改,推荐文件外置存储或压缩后入库并严格区分元数据与内容更新。

直接说结论:更新 LONGBLOB 或 LONGTEXT 字段本身不是瓶颈,真正卡住的是「每次更新都读全量再写全量」+「off-page 存储引发的额外 I/O 跳转」。不改访问模式,光调参数或换类型基本没用。
UPDATE 语句别碰大字段,除非你真要改它
常见错误现象:UPDATE doc SET title = ?, content = ? WHERE id = 1 中 content 是 LONGTEXT,但实际只改了 title —— 这会导致 MySQL 强制读取并重写整个 off-page 内容块,哪怕内容一字未变。
- 应用层应区分「元数据更新」和「内容更新」:元数据走独立 UPDATE(不含大字段),内容更新单独发请求
- 如果必须合并在一条 SQL 里,用
IF或触发器逻辑判断值是否变化,避免无意义覆盖(MySQL 不支持UPDATE ... SET x = IF(x != ?, ?, x)这种安全赋值,得靠应用控制) - 别在事务里混写大字段和高频小字段:大字段写入会延长行锁持有时间,拖慢并发更新
用 COMPRESS() 更新前先压缩,但注意字段长度和客户端解压
如果业务强要求大字段必须和主记录原子提交(比如审计日志),那更新时也得压缩,否则 COMPRESS() 增大体积可能被截断,后续 UNCOMPRESS() 返回 NULL。
- 字段类型至少用
MEDIUMBLOB(16MB)起步,BLOB(64KB)不够存压缩后数据 - 写入时用
UPDATE t SET data = COMPRESS(?) WHERE id = ?,不是先 SELECT 再应用层压缩 - 客户端读取后必须调用对应 zlib 解压(不能依赖 MySQL 的
UNCOMPRESS()在 SELECT 中计算,CPU 白耗且无法复用连接池缓存) - 已压缩的二进制(如 JPG、ZIP)再套
COMPRESS()几乎无效,反而增加 CPU 和延迟
批量更新大字段?先分拆,再异步
想一次 UPDATE ... WHERE id IN (1,2,3,...) 改几十个 LONGBLOB?这会瞬间打爆 max_allowed_packet,还让 buffer pool 缓存大量低频数据。
- 把批量操作拆成单条:应用层循环执行,每条带
WHERE id = ?,避免隐式临时表和长事务 - 对非实时场景(如后台归档),改用异步任务:先写消息队列,Worker 拉取后逐条处理,失败可重试
- 千万别在批量 UPDATE 中 SELECT 大字段做计算(比如
UPDATE t SET size = LENGTH(content))—— 这会强制加载全部 off-page 数据到内存
最该做的其实是删掉这个字段
95% 的所谓「必须存数据库」的大字段,真实约束只是「要和主记录关联」,而不是「要进 InnoDB 表空间」。路径字段 + 定时清理,比任何 SQL 优化都管用。
ALTER TABLE doc DROP COLUMN content; ALTER TABLE doc ADD COLUMN file_path VARCHAR(512) NOT NULL;- 应用上传文件后生成唯一名(如
uuid4()+ 扩展名),存到对象存储或本地/data/uploads/ - 更新时只改
file_path,查的时候由 Web 层读文件流返回,MySQL 完全不碰二进制 - 容易被忽略的点:文件清理必须独立于数据库事务,且要有幂等机制(比如按
file_pathSHA256 校验是否存在有效记录)



















