like 'abc%' 能走索引,'%abc' 必全表扫描;b+树仅支持最左前缀匹配,联合索引可优化范围查询,覆盖索引可避免回表;模糊搜索越界需换方案如fulltext或elasticsearch。

LIKE 'abc%' 能走索引,但 '%abc' 一定全表扫描
MySQL 的 B+ 树索引只支持最左前缀匹配,LIKE 'abc%' 会被优化成范围查询(name >= 'abc' AND name ),只要 <code>name 列上有索引,就能用上。而 LIKE '%abc' 或 LIKE '%abc%' 没有确定的起始位置,索引完全失效,只能逐行扫描。
常见错误现象:SELECT * FROM users WHERE name LIKE '%admin' 在百万级表上可能耗时数秒,EXPLAIN 显示 type=ALL、rows 等于总行数。
- 确认字段已建索引:
SHOW INDEX FROM users WHERE Key_name = 'idx_name'; - 前缀长度要合理:若
name大多以相同前 3 字开头,INDEX(name(10))比INDEX(name)更省空间且效果相当 - 避免在索引字段上套函数:
UPPER(name) LIKE 'ABC%'会跳过索引,应统一存储为大写并直接查name LIKE 'ABC%'
后缀匹配 '%abc' 的三种可行解法
硬要查结尾,又不想全表扫描,得绕开 B+ 树限制。不是“能不能”,而是“怎么换结构”。
- 用虚拟列 + 索引(MySQL ≥ 5.7.6):
ALTER TABLE users ADD COLUMN name_rev VARCHAR(255) AS (REVERSE(name)) STORED;,再建索引CREATE INDEX idx_name_rev ON users(name_rev);,查询改写为WHERE name_rev LIKE REVERSE('abc') + '%' - 全文索引不适用后缀场景,但可覆盖部分需求:
MATCH(name) AGAINST('abc' IN BOOLEAN MODE)只能查词,不能保证是结尾 - 反向索引需同步维护:如果业务允许,写入时就存一份
REVERSE(name)到冗余字段,并确保该字段有索引;否则线上UPDATE百万行反转值会锁表、耗时长
FULLTEXT 索引比 LIKE 快的本质不是“更高级”,而是换了索引类型
FULLTEXT 用的是倒排索引,LIKE 依赖的是 B+ 树——这是两类完全不同的数据结构。查 '数据库优化' 时,FULLTEXT 直接定位到含该词的所有行 ID;LIKE '%数据库优化%' 却要在每行字符串里做子串匹配,哪怕加了索引也无济于事。
- 创建前确认引擎和类型:
FULLTEXT仅支持InnoDB或MyISAM,字段必须是CHAR/VARCHAR/TEXT - 中文需调小分词粒度:
SET GLOBAL innodb_ft_min_token_size = 1;(需重启或动态生效取决于版本),否则“张三”被当停用词过滤 - 布尔模式才支持通配符:
AGAINST('张*' IN BOOLEAN MODE)中的*仅在布尔模式下有效,自然语言模式不认
别忽略 WHERE 条件顺序和联合索引设计
即使 LIKE 'abc%' 能走索引,如果前面还有低选择性条件,MySQL 也可能放弃它。比如 status = 0 AND name LIKE 'abc%',若 status 只有两个值,优化器大概率先扫 status = 0 子集再逐行匹配 name,而非走 name 索引。
- 高筛选性条件放前面:
created_at > '2025-01-01' AND name LIKE 'abc%'比反过来更可能触发索引合并或范围扫描 - 考虑联合索引:
INDEX(status, name)可让WHERE status = 1 AND name LIKE 'abc%'完整命中索引,避免回表 - 避免
SELECT *:若只需id和name,而INDEX(name)已覆盖这两列,就不用访问聚簇索引,速度更快
真正卡住性能的往往不是“会不会写 LIKE”,而是没意识到:B+ 树索引对模糊匹配有天然边界,越界就得换方案。要么约束查询形态(只允许前缀),要么换索引结构(FULLTEXT / 虚拟列),要么换系统(Elasticsearch)。选哪个,取决于你能否接受数据冗余、是否允许引入新组件、以及用户对“任意位置搜索”的真实容忍度。











