TEXT字段直接WHERE查询慢是因为MySQL默认不为TEXT建完整索引,InnoDB仅索引前768字节,超出部分无法参与查找,导致全表扫描;加普通B-tree索引意义极小,除非固定查前缀右模糊;高频全文模糊搜索应优先用FULLTEXT索引(InnoDB 5.6+),或通过摘要字段+前缀索引兜底;必要时拆表将TEXT移至附属表以减少主表I/O开销。

TEXT字段直接WHERE查询为什么慢得像卡住
因为MySQL对TEXT、MEDIUMTEXT这类大字段默认不建完整索引,全表扫描+逐行读取内容是常态。哪怕加了普通索引,InnoDB也只索引前768字节(utf8mb4下约255字符),超出部分完全不参与索引查找——你以为加了索引就快了,其实只是“假装快”。
常见错误现象:SELECT * FROM article WHERE content LIKE '%关键词%'执行几秒甚至几十秒;EXPLAIN显示type: ALL、key: NULL。
- 别在
content上直接建普通B-tree索引,意义极小——除非你总查前200字符 - LIKE左模糊(
'%关键词')或全模糊('%关键词%')无法走索引,不管有没有前缀长度 - 如果业务真要高频模糊搜全文,
FULLTEXT索引比前缀索引更合适,但仅限MyISAM或InnoDB(5.6+),且不支持LIKE语法,得用MATCH ... AGAINST
前缀索引该设多长?别拍脑袋填255
前缀长度不是越长越好。设太短,区分度低,索引失效;设太长,索引体积暴涨,缓存效率下降,甚至拖慢INSERT/UPDATE。关键看数据分布——比如10万条日志的user_agent字段,前20字符重复率可能高达40%,而前50字符才降到5%以下。
实操建议:
- 先用
SELECT COUNT(DISTINCT LEFT(content, N)) / COUNT(*) FROM table测不同N下的选择性(建议从10开始,每次+10试到100) - 选择性 > 0.95 才算合格,再往上加长度收益急剧下降
- 避免用
utf8mb4字段设过长前缀——100字符实际占400字节,InnoDB单页索引键有768字节硬限制 - 建索引时明确写长度:
CREATE INDEX idx_content_prefix ON article (content(60)),不写就是默认前768字节(可能溢出报错)
拆表不是玄学,什么时候必须把TEXT拎出去
当SELECT *或频繁查询不含TEXT字段的主干数据(如列表页只展示标题、时间、作者)时,大字段会严重拖慢查询和网络传输——InnoDB聚簇索引把所有列捆在一起存,哪怕你只SELECT三列,也要把整行(含几MB的content)从磁盘读上来再过滤。
典型场景:
- 文章列表接口返回
id、title、created_at,但content只在详情页用 - 统计类SQL(
COUNT、GROUP BY)完全不涉及TEXT字段 - 主表QPS高,但TEXT字段更新频次低(如发布后基本不变)
拆法很简单:CREATE TABLE article_body (article_id BIGINT PRIMARY KEY, content LONGTEXT),关联查时按需LEFT JOIN。注意外键约束和事务一致性——别让INSERT article成功但INSERT article_body失败。
LIKE模糊查TEXT,除了FULLTEXT还有没有更轻量的招
如果不想改查询语法、也不愿维护FULLTEXT索引(重建慢、停写久),可以靠生成摘要字段+前缀索引兜底。
做法:
- 新增
content_digest VARCHAR(255)字段,用SUBSTRING(content, 1, 255)或MD5(content)填充(后者适合去重判断) - 给
content_digest建普通索引,WHERE条件改成content_digest LIKE '关键词%'(仅支持右模糊) - 如果必须支持中英文混合搜索,可额外加
content_tokens字段,用应用层分词后存空格分隔的关键词(如“mysql 性能 优化”),再配合FIND_IN_SET或JSON_CONTAINS查 - 警惕
CONVERT(content USING utf8mb4)这类隐式转换——它会让索引彻底失效
真正难处理的是“内容中间某段包含关键词”这种需求,没索引支撑就只能接受慢,或者换Elasticsearch这类专用方案。别在MySQL里硬扛。










