LIKE 'abc%'能走索引,但受字段类型、排序规则、前缀选择性及隐式转换等影响可能失效;而'%abc'因B+树不支持后缀匹配,必定全表扫描,强制索引无效。

LIKE 'abc%' 为什么有时还是走不了索引?
不是写成 LIKE 'abc%' 就一定能走索引——优化器会基于实际数据分布和查询成本决定是否用索引。只要 EXPLAIN 中 type 是 ALL,说明它算完觉得扫表更快,哪怕你写了前缀匹配。
-
VARCHAR字段才可能生效:CHAR类型会自动补空格,比如索引里存的是'abc',但WHERE name LIKE 'abc%'实际可能匹配到'abc '(带空格),导致无法对齐索引值 - 排序规则必须一致:如果字段用的是
utf8mb4_0900_as_cs(大小写敏感),而你写WHERE name LIKE 'Abc%',优化器不会自动转成小写去查索引,得显式加COLLATE utf8mb4_0900_as_cs或统一写成小写 - 前缀区分度太低:比如
name LIKE 'A%'匹配了全表 40% 的行,优化器估算发现回表次数太多,随机 I/O 成本 > 顺序读聚簇索引,直接放弃索引。可用SELECT COUNT(DISTINCT LEFT(name, 1)) / COUNT(*)看选择性,低于 0.05 就危险
为什么 LIKE '%abc' 永远不走普通 B+ 树索引?
这不是 MySQL 的 bug 或配置问题,是 B+ 树结构本身的限制:它按字典序组织,只支持从某个确定前缀开始向后找;'%abc' 要求“以 abc 结尾”,意味着所有可能的前缀都要扫描一遍,根本没法定位起始页。
-
EXPLAIN里type必定是ALL,别试FORCE INDEX—— 强制也没用,B+ 树物理上就不支持这种跳转 - 反向索引(
name_rev VARCHAR(100) AS (REVERSE(name)) STORED+ 索引)只对LIKE '%abc'有效,因为可改写为name_rev LIKE 'cba%';对LIKE '%abc%'无效,反转后仍是LIKE '%cba%',照样没法用索引 - 全文索引也不是万能的:中文需
ngram分词(MySQL 8.0.30+ 内置),且MATCH(name) AGAINST('abc')不等价于LIKE '%abc%'—— 它是语义匹配,可能漏掉短词或边界情况
哪些写法会让 LIKE 'abc%' 暗中失效?
看着像前缀匹配,其实已经破坏了索引的可定位性。优化器不是看语法,而是看能否转化成 >= 'abc' AND 这类范围条件。
- 对字段用了函数:
UPPER(name) LIKE 'ABC%'→ 索引列被包装,原始值不可见,直接失效 - 隐式类型转换:
phone VARCHAR(20)却写WHERE phone = 13800000000,MySQL 会把每行phone转成数字比对,索引跳过 - 联合索引没用最左列:
INDEX(status, name),却只写WHERE name LIKE 'abc%',name不在最左,B+ 树无法按name排序建分支 - 范围查询后接等值:
WHERE name LIKE 'abc%' AND created_at > '2026-01-01' AND status = 'active',如果索引是(name, status, created_at),那status后面的created_at就用不上
覆盖索引能缓解,但解决不了根本问题
即使 LIKE 'abc%' 走了索引,SELECT * 仍要回表取整行数据;高并发下大量随机 I/O 可能拖垮性能。
- 把常用字段塞进索引:比如
CREATE INDEX idx_name_cover ON t (name, id, email, created_at),确保name在最左,后面字段按查询需要排 - 别堆太多字段:索引变大 → 写入慢、buffer pool 压力大、统计信息采样失真风险上升;查
information_schema.INNODB_INDEX_STATS看真实大小 - 真正卡住的,往往不是没建索引,而是业务需求本身越过了 B+ 树的能力边界——比如要求“标题任意位置含关键词”,那就该交给
FULLTEXT或 Elasticsearch,而不是在 SQL 层硬扛


















