LIKE '%abc%' 无法使用B+树索引,因其前导通配符破坏有序性,导致优化器无法确定扫描起点,只能全表扫描;应改用FULLTEXT索引配合MATCH...AGAINST查询。

为什么 LIKE '%abc%' 一定走不了 B+ 树索引
因为 B+ 树索引依赖有序性,而前导通配符(% 开头)让优化器无法确定扫描起点。即使字段有索引,MySQL 也只能全表扫描——这不是配置问题,是 B+ 树结构本身的限制。
常见误操作:ALTER TABLE t ADD INDEX idx_name (name) 后仍慢,就说明没绕过这个根本约束。
用 FULLTEXT 索引 + MATCH ... AGAINST 替代(适合长文本)
全文索引不依赖前缀匹配,而是对词元(token)建立倒排索引,天然支持中间匹配。但要注意:它默认只对长度 ≥ 4 的词建索引(ft_min_word_len=4),且对停用词、标点敏感。
- 建索引:
ALTER TABLE t ADD FULLTEXT(name) - 查询写法必须用:
SELECT * FROM t WHERE MATCH(name) AGAINST('abc' IN NATURAL LANGUAGE MODE)(不能用LIKE语法) - 若要精确匹配子串(如 'abc' 作为独立词出现),改用布尔模式:
AGAINST('+abc' IN BOOLEAN MODE) - 中文需额外配置:
innodb_ft_parser=ngram+ngram_token_size=2(见下节)
MySQL 5.7+ 的 ngram 分词器怎么配才生效
ngram 是 MySQL 内置的中文分词方案,把字符串按固定长度切片(如 ngram_token_size=2 时,'abc' → ['ab', 'bc']),再对切片建倒排索引。但它只作用于 FULLTEXT 索引,且必须显式启用。
- 建表时指定解析器:
CREATE TABLE t (name TEXT, FULLTEXT(name) WITH PARSER ngram) - 或建索引时指定:
ALTER TABLE t ADD FULLTEXT(name) WITH PARSER ngram -
ngram_token_size影响粒度:值越小,切得越细,查短词越准,但索引体积越大;查 'abc' 至少要设为 2 或 3 - 查询仍用
MATCH ... AGAINST,但支持更自然的中文子串意图,比如搜 '数据库' 可能命中 '关系型数据库' 中的 '数据库' 片段
真正需要 LIKE '%abc%' 语义时,别硬扛索引
如果业务强依赖任意位置的模糊匹配(比如日志关键词检索、用户输入即搜),又不想引入 Elasticsearch,可以接受一定延迟和资源开销,那直接上 WHERE name LIKE '%abc%' 并加 FORCE INDEX 没意义——不如提前剪枝:
- 加长度过滤:
WHERE LENGTH(name) >= 3 AND name LIKE '%abc%',减少扫描行数 - 用生成列 + 普通索引加速常见模式:
ALTER TABLE t ADD COLUMN name_abc TINYINT GENERATED ALWAYS AS (CASE WHEN name LIKE '%abc%' THEN 1 ELSE 0 END) STORED,再对name_abc建索引(适合固定关键词) - 高频子串可预计算哈希(如
MURMUR3)存到额外列,用等值查询替代模糊匹配
ngram 和 FULLTEXT 都不是万能的,它们改变的是“匹配单位”(从字符串到词元/切片),而不是绕过 MySQL 的查询执行模型。上线前务必用 EXPLAIN 看 type 是否变成 fulltext,以及 key 是否显示你建的全文索引名。










