like '%keyword' 慢是因为b+树索引不支持后缀匹配,需全索引或全表扫描;而like 'keyword%' 可利用最左前缀匹配快速定位。

LIKE '%keyword' 为什么慢,而 LIKE 'keyword%' 可以走索引
因为 MySQL 的 B+ 树索引是按字典序存储的,LIKE 'keyword%' 能利用索引的最左前缀匹配快速定位起始位置;但 LIKE '%keyword' 或 LIKE '%keyword%' 需要扫描全索引甚至全表——B+ 树不支持后缀或中缀的高效跳转。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,即使字段有普通索引也完全不生效。
- 只有前缀匹配(
LIKE 'abc%')才能用到普通索引,且要求查询条件不带函数、不参与表达式计算 - 如果字段是
TEXT或超长VARCHAR,还要确认索引长度是否足够(例如INDEX(col(191))) - 使用
COLLATE utf8mb4_0900_as_cs等大小写敏感排序规则时,LIKE行为仍受排序规则影响,不等于“精确匹配”
全文索引(FULLTEXT)适合哪些模糊场景
MySQL 的 FULLTEXT 索引本质是分词 + 倒排索引结构,专为自然语言搜索设计,适用于标题、文章正文等短文本字段的关键词召回,但不解决“后缀匹配”问题。
使用场景:用户搜“数据库优化”,希望命中含“MySQL 数据库性能优化”的记录;不是为了查“以‘优化’结尾”的字段值。
- 仅支持
MyISAM和InnoDB(5.6+),且字段类型需为CHAR/VARCHAR/TEXT - 默认最小词长为 4(
ft_min_word_len=4),搜“sql”会失败;需改配置并重建索引 -
MATCH(col) AGAINST('keyword' IN NATURAL LANGUAGE MODE)不支持通配符;IN BOOLEAN MODE支持+/-/*,但*只能放在词尾(optimiz*可匹配optimize,但*imize无效) - 停用词(如 “the”、“and”)会被忽略,无法强制搜索;自定义停用词表需重启 mysqld
倒排索引不在 MySQL 原生能力范围内,得靠外部方案补足
MySQL 没有提供可直接配置的通用倒排索引接口。所谓“用 MySQL 实现倒排”,实际是应用层模拟:拆词 → 写关联表 → 手动 JOIN 查询。这在高并发或高基数场景下极易成为瓶颈。
典型错误做法:建一张 post_tag 表存 post_id 和分词后的 tag,再用 WHERE tag LIKE '%xyz%' —— 这又回到了开头的性能陷阱。
- 真正可用的倒排能力来自 Elasticsearch、Meilisearch 或 SQLite 的
FTS5;MySQL 用户若强依赖后缀/中缀搜索,应考虑把这类查询路由出去 - 若必须留在 MySQL 内,可预生成 n-gram(如双字词)存入辅助表,用等值查询代替模糊匹配,但存储膨胀 3–5 倍,且更新成本高
-
GENERATED COLUMN + FULLTEXT是折中方案:例如对name字段生成反向字符串REVERSE(name),再对其建FULLTEXT,可间接支持后缀搜索(搜REVERSE('ing')→'gni'),但无法支持任意中缀
简单需求优先用前缀索引 + 应用层兜底
90% 的所谓“模糊查询”其实只需要前缀匹配或首字母筛选,比如搜索用户昵称、商品 SKU、订单号。这时候加个 INDEX(name(20)) 就够了,比折腾全文或倒排实在得多。
容易被忽略的点:当业务允许“输入即提示”时,不要等用户敲完再查,而是用 AJAX + LIMIT 10 做实时建议,既减轻 DB 压力,又提升体验。
- 避免在
WHERE中对字段用LOWER()、TRIM()等函数,否则索引失效;统一在写入时标准化(如全小写存储) - 字符集用
utf8mb4_unicode_ci而非_as_cs,减少排序开销;但要注意_ci下LIKE 'A%'会命中á、à等变体 - 如果字段区分度极低(如大量以 “test” 开头),即使有前缀索引,
SELECT ... WHERE name LIKE 'test%'仍可能回表严重,此时加覆盖索引更有效(INDEX(name, id, status))











