text字段上like '%xxx%'查询无法走索引,因b+树仅支持左前缀匹配;优化需据场景选全文索引、反向列、哈希虚拟列或拆表。

直接结论:TEXT字段上做LIKE '%xxx%'查询,不改结构就别指望快;全文索引、反向列、虚拟哈希、拆表——选哪个取决于你查什么、写多频、数据量多大。
为什么LIKE '%xxx%'在TEXT字段上永远不走索引
MySQL的B+树索引只能从左往右匹配,LIKE开头带%等于放弃起点,引擎必须逐行读取整条TEXT内容再做字符串扫描。哪怕你加了INDEX(content(100)),InnoDB也只索引前768字节(utf8mb4下约255字符),超出部分根本不进索引结构。EXPLAIN里type: ALL、key: NULL就是铁证。
常见错误现象:
- 查10万行表,
SELECT * FROM logs WHERE content LIKE '%error%'跑3秒以上 - 加了前缀索引但
EXPLAIN显示没用上,因为查询模式不匹配 - 误以为
FULLTEXT能替代所有LIKE,结果发现AGAINST('API')根本搜不到——默认ft_min_word_len=4把三字母词全过滤了
全文索引不是万能解,但调对参数才能生效
全文索引只对语义搜索友好,不是字符串包含判断工具。它分词、去停用词、按相关性排序,和LIKE逻辑完全不同。
实操要点:
- 先确认当前最小词长:
SHOW VARIABLES LIKE 'ft_min_word_len';,业务要搜'AI'或'go'就得设为2 - 动态修改后必须重建索引:
SET GLOBAL ft_min_word_len = 2;→ALTER TABLE articles DROP INDEX ft_content, ADD FULLTEXT(content); - 中文需指定解析器:
ADD FULLTEXT(content) WITH PARSER ngram(MySQL ≥ 5.7.6) - 别在
BLOB或JSON字段建全文索引——不支持 - 布尔模式下
AGAINST('+AI -test' IN BOOLEAN MODE)可用,但AGAINST('AI*')这种通配只在布尔模式生效,且不回溯原始字符串
后缀模糊(LIKE '%.pdf')用反向列最轻量
把后缀匹配转成前缀匹配,是唯一能用原生B+树索引解决后缀问题的办法。
关键步骤:
- 加生成列:
ALTER TABLE files ADD COLUMN path_rev VARCHAR(255) AS (REVERSE(file_path)) STORED; - 建索引:
CREATE INDEX idx_path_rev ON files(path_rev); - 查询改写:
SELECT * FROM files WHERE path_rev LIKE CONCAT(REVERSE('.pdf'), '%');(等价于file_path LIKE '%.pdf') - 写入一致性靠触发器或应用层保证,MySQL 8.0+可直接用
STORED生成列自动维护 - 注意字段长度:反转后若超索引页限制(768字节),建索引会报错,得配合前缀长度,如
(path_rev(100))
等值判断(“内容是否已存在”)用虚拟列哈希最省IO
真正拖慢查询的往往不是索引,而是每次SELECT *都强制读几MB的TEXT溢出页。哈希虚拟列让判断脱离大字段I/O。
操作逻辑:
- 加列:
ALTER TABLE logs ADD COLUMN content_md5 CHAR(32) AS (MD5(content)) STORED; - 建索引:
CREATE INDEX idx_content_md5 ON logs(content_md5); - 查询必须带双重校验:
SELECT * FROM logs WHERE content_md5 = MD5('xxx') AND content = 'xxx';(后半段防哈希碰撞,不能省) - MD5是32字符固定长,比SHA256更省内存;若担心碰撞概率,可用
SHA2(content, 256)但索引体积翻倍 - 此法对模糊搜索无效,只适用于精确内容判重
最易被忽略的一点:哪怕用了全文索引或反向列,只要查询里写了SELECT *且TEXT字段在行内,InnoDB聚簇索引仍会把整行(含溢出页指针)一起加载——拆表仍是减少主表I/O的终极手段。











