blob和text字段必须显式选型,因其在长度前缀、溢出机制、索引限制、缓存影响及日志体积等方面存在本质差异,随意选用会导致存储浪费、性能下降甚至操作失败。

为什么BLOB和TEXT字段必须显式选型而非“随便用一个”
MySQL的TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB以及对应TEXT变体,不只是容量不同——它们在行内存储开销、溢出页指针长度、甚至InnoDB页内预留空间策略上都不同。比如BLOB字段用2字节存长度前缀,而MEDIUMBLOB用3字节;若实际数据常在60KB左右,选LONGBLOB不仅浪费4字节前缀,还可能因超出行大小限制(≈65KB)触发隐式分页失败。
- 实际写入值接近上限时,优先选刚好覆盖的类型(如15MB文本用
MEDIUMTEXT,别用LONGTEXT) -
TINYBLOB/TINYTEXT只适合元数据级内容(如图标base64前缀、短摘要),超过255字节就直接失效 - InnoDB默认
ROW_FORMAT=COMPACT下,BLOB类字段超768字节即溢出;改用ROW_FORMAT=DYNAMIC可让所有大字段统一用20字节指针,但需确认MySQL版本≥5.7且表已重建
如何避免BLOB/TEXT字段污染Buffer Pool
大字段读取会把整块溢出页加载进Buffer Pool,挤占热点数据空间。即使只查id和title,若SQL写了SELECT *,InnoDB仍要跳转读溢出页——这是缓存命中率骤降的主因。
- 建表时强制指定
ROW_FORMAT=DYNAMIC:ALTER TABLE log ROW_FORMAT=DYNAMIC;,再配合innodb_file_format=Barracuda(MySQL 5.7+已默认) - 禁止对BLOB/TEXT字段做
ORDER BY或GROUP BY,否则MySQL会把整字段拉进sort_buffer,极易OOM - 应用层查询必须显式排除大字段:
SELECT id, status, updated_at FROM document,而非SELECT * - 监控
SHOW ENGINE INNODB STATUS里的Buffer pool hit rate,低于99%且Pages read ahead激增,大概率是溢出页随机I/O拖累
BLOB字段读写时JDBC常见报错及绕过方式
JDBC驱动对BLOB处理有隐式约束:比如setBinaryStream()某些重载未实现,或getBinaryStream()返回流被提前关闭,导致java.io.IOException: Stream closed。
- 写入前确认
max_allowed_packet足够大(例如存10MB文件需设为12M),否则报错Packets larger than max_allowed_packet are not allowed - 读取时别用
rs.getBinaryStream("img")后直接传给前端——流是一次性的,应先复制到ByteArrayOutputStream再转byte[] - Java中避免用
PreparedStatement.setBlob(int, Blob),改用setBinaryStream(int, InputStream, length)并明确传入长度,防止驱动内部误判截断 - 字符集无关:BLOB不走字符集转换,但客户端连接URL里
characterEncoding参数对BLOB无影响,别为此调错配置
TEXT字段索引与查询优化的真实约束
TEXT字段不能直接建普通索引,但前缀索引又受限于字符集。UTF8MB4下100字符可能占400字节,而InnoDB单列索引前缀上限是767字节(MySQL 5.7)或3072字节(8.0+),实际能索引的字符数远少于预期。
- 建前缀索引必须算字节:
ALTER TABLE article ADD INDEX idx_content_200 (content(200)),这里的200是字符数,不是字节数;若字段含大量emoji,实际索引字节可能超限 - 全文索引(
FULLTEXT)仅支持MyISAM和InnoDB(5.6+),但要求最小词长(ft_min_word_len)默认4,短词搜不到 - 等值查询别依赖哈希——
MD5(content)生成虚拟列虽可行,但更新时需同步维护,且哈希碰撞风险在亿级数据下不可忽略 - 真正高频检索的字段,不如拆出独立
summary列(VARCHAR(500))存关键片段,既可索引又免溢出
LONGBLOB更新,binlog里会完整记录二进制内容,主从延迟和磁盘IO压力远超想象——这比查询慢更隐蔽,也更难监控。











