PS:给order表新增remark索引
sql
CREATE INDEX idx_remark ON orders(remark);
现在orders表中存在idx_user_id(user_id),idx_user_status(user_id, status),idx_remark(remark)三个索引
8-1.后置%
运行:
sql
EXPLAIN SELECT * FROM orders WHERE remark LIKE '71ae%';
结果为:
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | range | idx_remark | idx_remark | 1023 | 207 | 100 | Using index condition |
分析:
- type = range :表示这是一个范围扫描。因为
LIKE '71ae%'在索引上相当于一个范围条件:大于等于'71ae'且小于'71af'(实际上是以'71ae'开头的所有字符串)。所以MySQL会用索引做范围查找 - possible_keys = idx_remark :数据库识别到可以用
remark的索引 - key = idx_remark:实际使用的就是你刚建的索引
- rows = 207:预估只需要扫描207行,全表扫描已经被优化掉了
- filtered = 100:表示这207行全部符合条件
- Extra = Using index condition :说明使用了 Index Condition Pushdown (ICP) 优化:过滤条件直接在索引层完成,而不是先取出数据再过滤,这进一步减少了回表的开销
8-2.中间%
运行:
sql
EXPLAIN SELECT * FROM orders WHERE remark LIKE '%71ae%';
结果为:
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ALL | 99520 | 11.11 | Using where |
分析:
LIKE '%71ae%',索引无法使用。在 MySQL 中,B+Tree 索引必须从左侧开始匹配
因为索引 idx_remark 的结构类似:
71aa....
71ab....
71ac....
71ae....
71af....
如果查询:
LIKE '71ae%'
数据库可以直接定位:
71ae 开头的范围
但如果查询:
LIKE '%71ae%'
数据库不知道
71ae在字符串什么位置
总结:
- LIKE 'abc%' → 可以使用索引
- LIKE '%abc' → 无法使用索引
- LIKE '%abc%' → 无法使用索引
8-3.真实系统如何解决%xxx%查询
- 方案1:全文索引
MySQL提供:
FULLTEXT INDEX
适合:文章、评论、文本
- 方案2:搜索引擎
例如:Elasticsearch
适合:模糊搜索、关键词搜索
- 方案3:前缀搜索
系统设计时只允许:
prefix search
例如:订单号、用户ID、编码