LIKE 前导通配符致索引失效
场景痛点
用户搜索框输入关键词,后端用 LIKE '%zhang%' 模糊查询。username 字段有索引,但 20 万行全表扫描。
问题分析
sql
-- bad.sql: 前导通配符 % 使索引失效
SELECT id, username, nickname, phone, created_at
FROM t_user_search
WHERE username LIKE '%zhang%';EXPLAIN: type=ALL, key=NULL -- 全表扫描。
原因:B+ 树按字母顺序排列。前导 % 意味着无法确定扫描起点(匹配可能出现在任何位置),只能逐行扫描做子串匹配。
优化方案
sql
-- good.sql: 后导通配符,B+ 树可定位 'zhang' 前缀起点
SELECT id, username, nickname, phone, created_at
FROM t_user_search
WHERE username LIKE 'zhang%';EXPLAIN: type=range, key=idx_username -- 走索引范围扫描。
| 指标 | 优化前 (bad) | 优化后 (good) |
|---|---|---|
| 访问类型 | ALL | range |
| 使用索引 | NULL | idx_username |
| 扫描行数 | 200,000 | ~50 |
| 附加信息 | Using where | Using index condition |
🚀 前导% -> 后导%,索引恢复范围扫描
注意
'zhang%' 只匹配以 zhang 开头的用户名,语义与 '%zhang%'(包含 zhang)不同。若业务确需包含匹配,考虑:
- MySQL FULLTEXT 全文索引
- Elasticsearch / MeiliSearch 等外部搜索引擎
- 如必须用 LIKE '%x%',接受全表扫描但限制结果集
LIMIT
避坑指南
注意事项
'keyword%'(后导%)走索引 ✅'%keyword'(前导%)不走索引 ❌'%keyword%'(双向%)不走索引 ❌LIKE CONCAT(?, '%')动态拼接时,参数在前也走索引 ✅
本地复现
bash
./scripts/run-case.sh 05-like-leading-wildcard