结论:不要无脑用longtext,压缩必须分层做——表级压缩打底,应用层压缩兜底,compress()/uncompress()仅适用于小批量、低频写入场景;其返回blob不可索引、需显式解压、频繁读取时cpu开销翻倍,且超50kb大文本压缩率常低于30%。

直接说结论:不要无脑用 LONGTEXT,压缩必须分层做——表级压缩打底,应用层压缩兜底,COMPRESS() 函数只适合小批量、低频写入场景。
什么时候该用 COMPRESS() 和 UNCOMPRESS()
这两个函数是 MySQL 8.0+ 提供的“手动压缩”能力,但它们不是万能胶水。
- 适用场景很窄:比如日志归档表的一次性导入、后台批处理任务中对单条
TEXT字段做轻量压缩(COMPRESS('hello world')),且后续基本不更新 -
COMPRESS()返回的是BLOB,不能直接用于WHERE条件查询,也不能建索引;解压必须显式调用UNCOMPRESS(),否则查出来是乱码二进制 - 性能代价明显:每次
SELECT都要解压,CPU 开销翻倍;如果字段频繁读取,反而拖慢整体响应 - 错误现象典型:
SELECT content FROM logs WHERE id = 1返回一堆不可读字节,或UNCOMPRESS(content)返回NULL(说明原始数据损坏或未压缩)
ROW_FORMAT=COMPRESSED 的真实效果和坑
这是 InnoDB 表级压缩,比函数更透明,但配置不当等于白开。
- 必须搭配
KEY_BLOCK_SIZE使用,常见值为1、2、4、8(单位 KB),值越小压缩率越高,但随机读性能越差;KEY_BLOCK_SIZE=8是较稳妥的起点 - 仅对页内数据有效——大文本字段若超过一个页(默认 16KB),就会跨页存储,压缩率断崖下跌;实测
LONGTEXT超过 50KB 后,压缩率常低于 30% - 启用后
SHOW CREATE TABLE看不到压缩标记,得用SHOW TABLE STATUS查Row_format和Data_length对比验证 - 不兼容某些操作:比如在线 DDL 修改列类型可能失败;备份工具(如
mysqldump)默认不识别压缩页,需加--compress或改用物理备份
应用层压缩才是主力方案
真正扛住高并发读写的,是把压缩逻辑提到代码里,数据库只存二进制 blob。
- 推荐组合:Python 用
zlib.compress()/zlib.decompress(),Java 用GZIPOutputStream,Go 用gzip.Writer;避免用 base64 编码再存,纯属浪费空间 - 字段类型必须改用
BLOB或LONGBLOB,不能用TEXT—— 否则字符集转换会破坏二进制数据 - 加个校验字段防损坏:比如同时存
content_compressed BLOB和content_crc32 INT UNSIGNED,写入时算一次 CRC,读取后校验再解压 - 注意事务边界:压缩/解压在应用内存完成,不增加数据库负担;但也要警惕 OOM——单条超 10MB 文本压缩前先切块或流式处理
别忽略 LOB 存储机制本身带来的 I/O 问题
哪怕压缩了,TEXT/BLOB 的物理存储方式仍会拖慢查询。
- InnoDB 默认把大字段存在溢出页(off-page),主行只留 20 字节指针;
SELECT *会强制触发额外 I/O 加载这些页,哪怕你根本不需要那字段 - 解决方案极简单:永远用明确字段列表代替
*,例如SELECT id, title, created_at FROM docs,避开content列 - 如果前端只要摘要,就提前在写入时生成
summary VARCHAR(500)并建索引;全文检索别硬刚 MySQL,交给Elasticsearch或Meilisearch - 分区表对大文本帮助有限——按时间分区能加快删旧数据,但单分区内的 LOB 读取瓶颈仍在,别指望靠分区解决性能问题
真正难的不是选哪个压缩方法,而是判断哪部分数据值得压缩、哪部分该拆出去、哪部分干脆不该进数据库。比如用户上传的 PDF 原文,存文件系统 + 数据库存路径,比塞进 LONGBLOB 强十倍。











