MySQL Like
LIKE 用于模糊查询字符串:
- %:0~∞个任意字符
- _:1个任意字符
常见示例:
col LIKE 'abc%' -- 右匹配
col LIKE '%abc%' -- 全模糊
col LIKE 'ab_c' -- 单字符通配
MySQL 对 LIKE 有明确优化规则:
| 模式 | 是否使用索引 | 底层行为 |
|---|---|---|
| ‘xxx%’ | ✅ 可使用 B+Tree 索引 | 范围扫描(range) |
| ‘%xxx’ | ❌ 索引失效 | 全表扫描 |
| ‘%xxx%’ | ❌ 索引失效 | 全表扫描 |
| ‘x%y%z%’ | 部分可能使用前缀,但整体退化 | 大概率不走索引 |
| LIKE BINARY ‘xxx%’ | 使用索引但区分大小写 | range |
| col LIKE ‘xxx%’ AND col >= … | 可强制走索引 | range |
结论:
- LIKE 只有“右匹配”能走索引
- 即 col LIKE ‘abc%’ 能用索引,其它情况大多全表扫描。
因为 B+Tree 索引从 第一个字符开始比较。
‘%xxx’ 无法确定开头的字符 -> 索引范围未知 -> 无法走 B+Tree -> 只能退化为 全表逐行匹配。
EXPLAIN SELECT * FROM user WHERE name LIKE '%abc%';
结果一般是:
| type | key | rows | Extra |
|---|---|---|---|
| ALL | NULL | 1e6 | Using where |
表示 全表扫描 ALL(最慢)。
如果使用右匹配:
EXPLAIN SELECT * FROM user WHERE name LIKE 'abc%';
结果:
| type | key | rows | Extra |
|---|---|---|---|
| range | idx_name | 100 | Using index condition |
性能差距可达 1000 倍。
name LIKE 'abc%'
索引利用率最高,推荐为“生产安全级用法”。
如果你能确定是右匹配,但 MySQL 因统计信息错误没走索引,可使用:
SELECT * FROM user FORCE INDEX(idx_name)
WHERE name LIKE 'abc%';
WHERE name LIKE 'abc%' AND name >= 'abc' AND name < 'abd'
可帮助优化器更明确范围。
错误(索引失效):
WHERE age LIKE '30'
正确:
WHERE CAST(age AS CHAR) LIKE '30'
但通常不要对数字字段用 LIKE。
WHERE LOWER(name) LIKE 'abc%'
会导致索引失效。
WHERE name LIKE CONCAT('%', 'abc', '%')
仍然是 ‘%abc%’ -> 无法走索引。
中文模糊匹配建议使用:
- ElasticSearch
- Mroonga
- Fulltext index(全文索引) 不适用于中文
下面几种方法是企业强烈推荐的处理方案:
适合复杂模糊搜索 / 多字段搜索。
这是生产中真正的推荐方式。
ALTER TABLE article ADD FULLTEXT(title, content);
搜索:
SELECT * FROM article
WHERE MATCH(title, content) AGAINST('linux');
缺点:对中文效果差。
使用 Mroonga 或 MySQL 8 窗口函数配合分词。
如果你的数据前两位有规律:
INDEX idx_name_prefix(name(3))
配合:
WHERE name LIKE 'abc%'
但不能解决 ‘%xxx%’。
为了支持后缀匹配 %xxx,可以存一个反转字符串。
ALTER TABLE t ADD COLUMN name_reverse VARCHAR(255);
UPDATE t SET name_reverse = REVERSE(name);
CREATE INDEX idx_name_reverse ON t(name_reverse);
查询 %abc:
SELECT * FROM t
WHERE name_reverse LIKE REVERSE('abc') || '%';
可以走索引(企业大量使用的技巧)。
以 1000w 行表测试:
| 条件 | 是否走索引 | 耗时 |
|---|---|---|
| name LIKE ‘abc%’ | ✅ | 3ms |
| name LIKE ‘%abc’ | ❌ | 2s |
| name LIKE ‘%abc%’ | ❌ | 3s |
| MATCH AGAINST(全文) | ✅ | 15ms |
| ElasticSearch | ✅ | 1~5ms |
差距是几十到几百倍。
MySQL LIKE 是否走索引的根本逻辑:必须从左边开始匹配。只要左边不是固定字符串(不是 ‘xxx%’),就不能走 B+Tree 索引。