mysql查longtext慢主因是innodb读行时被迫跳页加载溢出页,即使select未含text字段,因优化器误判或mvcc版本检查仍会触发随机i/o;有效解法仅三类:拆表、改dynamic格式、冷热分离。

MySQL查LONGTEXT慢,根本不是“数据太大所以慢”,而是InnoDB在读行时被迫跳页——主记录里只存20字节指针,真实内容在别的页上,每次访问都得额外做一次随机磁盘I/O。
为什么SELECT id, name也会被TEXT拖慢
即使SQL里完全没写content字段,只要表结构里存在未被EXCLUDE的TEXT/LONGTEXT列,InnoDB仍可能加载整行:
- 优化器统计信息不准(比如
AVG_ROW_LENGTH虚高),误判索引覆盖不全,放弃使用idx_last_update_time这类二级索引 - READ-COMMITTED隔离级别下,MVCC需检查undo链上的多个版本,而版本记录可能跨页存储,触发溢出页读取
-
ROW_FORMAT=COMPACT或REDUNDANT时,只要该字段长度>768字节,就必然溢出——哪怕你只插了1KB文本
验证方法:SHOW CREATE TABLE t\G看ROW_FORMAT;再跑SELECT LENGTH(content) FROM t ORDER BY LENGTH(content) DESC LIMIT 5确认实际分布。
EXPLAIN显示type=index但查询仍慢的真相
这种现象很典型:执行计划看似走了索引(type=index),key列也非NULL,但响应时间依然秒级起步。原因在于:
- 该索引是
last_update_time单列索引,但SELECT id, name, last_update_time需要回表——而回表要读聚簇索引页,该页里含LONGTEXT指针,InnoDB必须加载整行才能提取id和name - 即使
id和name本身很小,InnoDB也不会“只读前几个字段”,它按行粒度加载,遇到溢出指针就顺手把溢出页也拉进来 -
Handler_read_rnd_next值异常高(用SHOW PROFILE FOR QUERY N查),基本就是溢出页随机读的铁证
别信“加了索引就安全”——关键看索引是否覆盖(covering),即是否包含所有SELECT字段。对含大字段的表,INCLUDE字段进索引不现实,只能拆或绕。
真正有效的三类绕过溢出页操作
目标不是“让TEXT快起来”,而是“不让它参与这次查询”。实操路径只有三条,没有第四条:
-
拆表:新建
t_content(id PK, content LONGTEXT),原表删掉content。查询时用SELECT t.id, t.name FROM t LEFT JOIN t_content USING(id)——仅当真需要内容时才JOIN,否则零开销 -
改ROW_FORMAT=DYNAMIC:MySQL 5.7+默认支持,但必须显式指定建表语句里的
ROW_FORMAT=DYNAMIC,且确保innodb_file_per_table=ON。这样≤½页(约8KB)的内容会优先存本地,避免指针跳转;超长仍溢出,但至少覆盖90%中等长度场景 -
冷热分离:把历史
content导出到S3/MinIO,数据库只留content_url和content_hash。适合日志、评论、附件类数据——应用层加一层fetch逻辑,比硬扛在InnoDB里靠谱得多
别试“给TEXT加前缀索引”来加速SELECT——前缀索引只影响WHERE和ORDER BY,对读取本身毫无帮助;也别指望SELECT id, name能自动避开溢出字段,InnoDB没这个智能。











