like '%keyword'无法使用索引,因b+树仅支持前缀匹配;全文索引适用于自然语言字段的模糊搜索,但需match...against语法;后缀匹配可用反转生成列+前缀索引绕过。

LIKE '%keyword' 为什么走不了索引
MySQL 对 LIKE 的索引使用有硬性限制:只有前缀匹配(LIKE 'keyword%')才能用上 B+ 树索引;一旦开头带 %,比如 LIKE '%keyword' 或 LIKE '%key%',优化器会直接放弃索引,转为全表扫描。
根本原因是 B+ 树索引按字典序存储,只能高效支持“从某前缀开始往后找”,没法反向“从结尾往前匹配”。你查 '%abc',数据库得把每一行字段值都拿出来截取后缀比对,自然没法跳过数据。
- 常见错误现象:
EXPLAIN显示type=ALL、key=NULL,哪怕字段上有索引 - 注意区分大小写:如果字段是
utf8mb4_bin排序规则,LIKE区分大小写,但不影响索引是否生效 - 哪怕加了
FORCE INDEX,也强制不了——语法允许,但执行时仍被忽略
全文索引(FULLTEXT)适合哪些模糊场景
FULLTEXT 索引专为文本搜索设计,能高效处理含中间或后缀的关键词匹配,但它只在 MyISAM 和 InnoDB(5.6+)中支持,且仅适用于 MATCH ... AGAINST 语法,不兼容 LIKE。
它不是“万能替代”,而是有明确适用边界:
- 适用场景:文章标题、正文、商品描述等自然语言字段,搜索词通常是独立词语(如
AGAINST('数据库 优化')),支持布尔模式、自然语言模式 - 不适用场景:手机号、身份证号、短编码类字段(分词后无意义),或需要精确字符级匹配(如
'ab12c'中找'12') - 建索引要显式声明:
ALTER TABLE t ADD FULLTEXT INDEX ft_name (content);查询必须用MATCH(content) AGAINST('xxx' IN NATURAL LANGUAGE MODE) - 注意默认停用词:像 “the”、“is” 这类词会被忽略,短于 4 字符的词(默认)也不索引,可通过
ft_min_word_len调整
前缀索引 + 计算列(Generated Column)绕过 % 开头
如果业务确实需要后缀匹配(如查所有以 '.pdf' 结尾的文件名),又不想改查询逻辑,可以用“把后缀提前”的方式骗过索引限制。
核心思路:新增一个生成列,存字段的反转值,再给它建普通索引。查 '%pdf' 就变成查 REVERSE(filename) LIKE 'fdp%' —— 这就又变回前缀匹配了。
ALTER TABLE docs
ADD COLUMN filename_rev VARCHAR(255)
GENERATED ALWAYS AS (REVERSE(filename)) STORED,
ADD INDEX idx_filename_rev (filename_rev);
- 查询写法:
SELECT * FROM docs WHERE REVERSE(filename) LIKE REVERSE('%pdf')→ 实际是LIKE 'fdp%' - 必须用
STORED生成列,因为只有它能建索引;VIRTUAL列不行 - 注意函数确定性:
REVERSE()是确定性函数,安全;但自定义函数或NOW()这类就不行 - 空间开销:多存一份反转字符串,字段越长、行越多,额外存储越大
LIKE 模糊查询还能怎么压性能?
当实在绕不开 LIKE '%xxx%',又无法换引擎或加全文索引时,就得靠外围手段控损:
- 加 LIMIT:哪怕只查前 10 条,也比扫完整表快得多;但注意
OFFSET大时依然慢(需先跳过 N 行) - 缩小扫描范围:用其他高选择性条件前置过滤,比如
WHERE status = 1 AND name LIKE '%xxx%',让status先筛掉 90% 数据 - 避免 SELECT *:只查必要字段,减少 IO 和网络传输;尤其别在模糊查询里
SELECT blob_field - 考虑应用层缓存:如果结果不常变,把常见模糊关键词的结果缓存起来(如 Redis 存
search:pdf:2024),比反复查库稳得多
真正难的不是写对 SQL,是判断“这个模糊查到底该不该在数据库里做”——很多所谓“优化”,其实是把问题从 DB 搬到 ES 或内存计算里。别死磕 LIKE,先想清楚它是不是本该由更合适的工具来扛。











