绝大多数场景下,LONGBLOB 不该进数据库,应存文件系统或对象存储(如 OSS、S3),表中仅保存路径或 URL;必须存时优先压缩再入库,并用 TEXT 替代 BLOB 存文本数据,查询务必显式指定字段或裁剪内容。

用文件系统存 BLOB,而不是 MySQL
绝大多数场景下,LONGBLOB 不该进数据库 —— 它不是设计来扛 1MB+ 二进制数据的。MySQL 的 InnoDB 对大字段采用 off-page 存储(主索引页只留 20 字节指针),每次读取都要额外 I/O 跳转,且无法利用操作系统 page cache,反而加重 buffer pool 压力。
真实可行的做法是:把文件写到磁盘或对象存储(如 OSS、S3),在表里只存路径或 URL:
-
ALTER TABLE doc ADD COLUMN file_path VARCHAR(512),删掉原content LONGBLOB列 - 应用层上传文件后,生成唯一文件名(如
uuid4()+ 扩展名),保存到/data/uploads/或 CDN 可达路径 - 写入时只 INSERT 路径,查询时由 Web 服务读取文件流并返回,不经过 MySQL 网络传输
- 注意权限和清理:设置定时任务清理 90 天未关联的文件,避免磁盘涨满
非存不可时,优先压缩再入库
如果业务强依赖“BLOB 必须在事务内写入”(比如审计日志需与主记录原子提交),那必须压缩后再存。MySQL 自带 COMPRESS() 函数可用,但要注意它返回的是 zlib 格式二进制,客户端读取时得对应解压。
常见错误现象:SELECT UNCOMPRESS(content) 返回 NULL —— 多半是插入时没用 COMPRESS(),或者字段类型不够长(COMPRESS() 可能略增大体积,MEDIUMBLOB 比 BLOB 更稳妥)。
- 写入:
INSERT INTO log (msg) VALUES (COMPRESS('原始日志文本')) - 读取:应用层调用
UNCOMPRESS(),或在 SELECT 中直接用(但会增加 CPU 开销) - 别对压缩后字段建索引 —— 二进制无语义,
LIKE和前缀索引都失效 - 压缩率取决于内容:纯文本通常 60%~80%,已压缩图片/视频再压几乎没收益,还白耗 CPU
TEXT 比 BLOB 更适合存大文本,别混用
如果你实际存的是 HTML、JSON、日志等可读文本,用 MEDIUMTEXT 替代 MEDIUMBLOB 是最简单有效的性能提升。BLOB 按字节处理,不走字符集校验;TEXT 支持 utf8mb4、可建前缀索引、能参与 ORDER BY,且 InnoDB 对 TEXT 的 off-page 处理更成熟。
容易踩的坑:ALTER TABLE t MODIFY content MEDIUMTEXT 直接执行会失败,如果原字段含非法字节(比如某次误插了 PDF 二进制),MySQL 拒绝转换。必须先清洗:
- 查出异常行:
SELECT id FROM t WHERE content REGEXP '[\x00-\x08\x0E-\x1F\x7F-\x9F]'(粗筛控制字符) - 导出问题数据,用 Python 或 Perl 清洗后再批量 UPDATE
- 确认无误后执行
MODIFY,并补上字符集:MODIFY content MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci
查询时绕过 BLOB/TEXT 字段加载
哪怕只查一行,SELECT * 遇到 MEDIUMTEXT 也会把整块内容从磁盘加载进连接内存,再发给客户端 —— 并发稍高就 OOM。优化核心就一条:永远显式指定字段,且对大字段做裁剪。
- 错:
SELECT * FROM article WHERE id = 123→ 可能加载 5MB 内容进内存 - 对:
SELECT id, title, SUBSTRING(content, 1, 500) AS preview FROM article WHERE id = 123 - 更稳:
SELECT id, title, created_at FROM article WHERE id = 123,内容由单独接口按需拉取 - 加虚拟列加速过滤:
ALTER TABLE article ADD COLUMN content_md5 CHAR(32) AS (MD5(content)) STORED,然后CREATE INDEX idx_md5 ON article(content_md5)
真正难处理的从来不是怎么存,而是怎么让查询不拖垮整个实例 —— 字段裁剪、虚拟列、外置存储,这三招覆盖了 95% 的线上问题。剩下那些非要实时查完整 BLOB 的场景,得先确认是不是架构设计本身出了问题。



















