本文目录导读:

在PHP项目(或任何数据库项目)中使用 LIKE 查询时,如果模式以 开头(%keyword 或 %keyword%),数据库索引通常会失效,导致全表扫描,这是由 B+树索引的物理存储结构决定的。
核心原因:B+树索引的“最左前缀”原则
大多数关系型数据库(MySQL、PostgreSQL等)的索引默认使用 B+树 结构。
- B+树索引的工作方式:索引是按照从左到右的顺序对数据进行排序和存储的,当执行查询时,数据库会从索引树的根节点开始,根据值的前缀逐步向下查找。
- LIKE 'keyword%'(尾匹配)为何能走索引:
LIKE 'abc%'相当于要求“所有以abc开头的值”,数据库引擎可以拿着abc这个前缀,直接在B+树中进行范围扫描(从abc开始,到abd结束),这本质上是等值查询 + 范围查询,完全符合最左前缀原则。 - LIKE '%keyword'(首匹配)为何不走索引:
LIKE '%abc'相当于“所有以abc结尾的值”,数据库无法确定前缀是什么,索引中可能存储了xabc、yabc、zabc,要找到所有以abc结尾的记录,数据库必须遍历整个索引树的叶子节点,逐一检查每个值是否以abc遍历整个索引通常比全表扫描更慢(因为索引回表查询的额外开销),所以优化器会选择直接全表扫描。 - LIKE '%keyword%'(模糊匹配)为何不走索引:同理,数据库不知道开头是什么,也无法利用B+树的有序性进行快速定位,它只能全索引扫描或全表扫描。
特殊情况(MySQL为例)
在特定条件下,LIKE '%keyword' 可能利用 覆盖索引 或 索引条件下推。
-
覆盖索引(Covering Index):
- 如果查询的所有字段都包含在同一个索引中,且查询只返回这些字段,数据库可能仍然会扫描整个索引,但不会回表,这比全表扫描快一些,但本质仍是全索引扫描(慢)。
SELECT id FROM users WHERE name LIKE '%abc'(假设id和name都在联合索引中)。
-
索引条件下推(ICP,Index Condition Pushdown,MySQL 5.6+):
- 对于 InnoDB 存储引擎,
LIKE '%keyword'虽然无法利用索引的排序特性,但 MySQL 可以将WHERE条件直接推送到存储引擎层,存储引擎在扫描索引的过程中,会提前过滤掉不符合LIKE '%abc'条件的行,减少回表次数,但索引本身仍然需要全扫描。
- 对于 InnoDB 存储引擎,
总结一句话: 在开头时,数据库无法从B+树根节点确定搜索的起始位置,只能暴力扫描,所以不走索引。
解决方案
如果必须使用首尾模糊查询且需要高性能,可以考虑以下方案:
| 方案 | 原理 | 适用场景 | 缺点 |
|---|---|---|---|
| 全文索引(Full-Text Index) | 使用倒排索引(Inverted Index),专门为模糊搜索/分词搜索设计,语法:MATCH (col) AGAINST ('keyword') |
大量文本搜索、搜索引擎类型功能 | 只支持 MyISAM/InnoDB;对中文需要分词器;不支持精确前缀匹配 |
| 反向索引(Reverse Index) | 存储字段的反转字符串,查询 LIKE '%abc' 时,改为 LIKE 'cba%' 查反向字段。 |
常用后缀查询(如邮箱域名搜索 @gmail.com、路径搜索) |
需要维护反转后的字段;不支持中间匹配 |
| ES / Sphinx | 外部搜索引擎(Elasticsearch、Sphinx),专门做模糊查询和分词。 | 大数据量、高并发、复杂模糊搜索 | 增加系统复杂度,需要额外运维 |
| 变更业务逻辑 | 将 %keyword% 改为 keyword%(前缀匹配)。 |
很多场景下,用户习惯从左到右输入(如商品名、用户名) | 限制用户输入习惯,可能无法满足需求 |
| 使用 Like 加覆盖索引 | 确保查询字段(WHERE name LIKE '%abc')和 SELECT 字段全被覆盖。 |
非常小的数据量或表中只有少数几行 | 不通用,数据量大时性能仍差 |
最佳实践建议
- 优先使用前缀匹配:
LIKE 'keyword%'而非%keyword%。 - 考虑全文索引:对文本搜索需求,使用
FULLTEXT INDEX。 - 数据量较小时(万级别):即使
%keyword%全表扫描,在内存足够的情况下,MySQL 也能很快响应,不要过早优化。 - 数据量大时:务必使用外部搜索引擎或变更为可索引的查询方式。
LIKE '%keyword%' 不走索引是由B+树的内存结构决定的,不是bug,解决方案取决于你的具体业务场景和数据量。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播