mysql中如何优化LIKE语句的索引使用

1次阅读

LIKE 以通配符开头时索引完全失效,因 B + 树无法利用有序性定位,仅 ’abc%’ 和 ’ab_c’ 可走索引;可用前缀索引、覆盖索引或 FULLTEXT 索引优化,并避免隐式类型转换。

mysql 中如何优化 LIKE 语句的索引使用

LIKE 以通配符开头时索引完全失效

WHERE name LIKE '%abc'WHERE name LIKE '%abc%' 这类查询出现时,MySQL 无法使用 B+ 树索引的有序性,会直接走全表扫描。这是因为索引是按字典序存储的,而前导通配符让数据库无法确定从哪个叶子节点开始查找。

  • 唯一能走索引的 LIKE 形式是 WHERE col LIKE 'abc%'(前缀匹配),此时可利用索引快速定位到 'abc' 开头的所有记录
  • WHERE col LIKE 'ab_c'(单字符通配)也能用索引,因为长度固定、前缀明确
  • 注意:即使字段有索引,只要左侧带 %EXPLAIN 中的 type 字段就会显示为 ALL

用覆盖索引 + 前缀索引缓解性能问题

如果业务必须支持模糊搜索但又不能全表扫,可以结合字段特性做针对性优化。例如用户昵称搜索,通常只关心前 20 个字符:

ALTER TABLE users ADD INDEX idx_nickname_prefix (nickname(20));

这样 WHERE nickname LIKE '张 %' 就能命中该前缀索引,节省空间且提升查询效率。但要注意:

  • 前缀长度不能太短(否则区分度低,大量回表),也不能太长(浪费索引空间、降低写入性能)
  • SELECT COUNT(DISTINCT LEFT(nickname, 20)) / COUNT(*) 估算区分度,理想值 > 0.9
  • 覆盖索引对 LIKE 'xxx%' 有效,但若查询中包含未建索引的字段(如 SELECT id, email FROM users WHERE nickname LIKE '张 %'),仍需回表

全文索引替代 LIKE 百分号匹配

对描述类、内容类字段(如文章标题、商品详情),LIKE '%keyword%' 几乎必然慢,应改用 FULLTEXT 索引 + MATCH …… AGAINST

ALTER TABLE articles ADD FULLTEXT(title, content); SELECT * FROM articles WHERE MATCH(title, content) AGAINST('数据库优化' IN NATURAL LANGUAGE MODE);

注意点:

  • 仅 MyISAM 和 InnoDB 支持,InnoDB 要求 MySQL ≥ 5.6
  • 默认忽略少于 4 字符的词(ft_min_word_len),中文需配合 ngram 插件或使用第三方方案(如 Elasticsearch)
  • AGAINST('xxx*' IN BOOLEAN MODE) 支持右截断(类似 LIKE 'xxx%'),但不支持左截断

避免 隐式类型转换 导致索引失效

一个常被忽略的坑:LIKE 字段类型与参数类型不一致时,MySQL 可能自动转换,导致索引失效。例如:

  • phone VARCHAR(20) 有索引,但写成 WHERE phone LIKE 138%(数字字面量),MySQL 会把 phone 转成数字比较,索引失效
  • 正确写法是 WHERE phone LIKE '138%',确保两边都是字符串
  • 同理,COLLATE 不一致(如字段是 utf8mb4_0900_as_cs,参数没指定)也可能触发 隐式转换

执行前务必用 EXPLAIN 看一眼 keyrows,哪怕语句看起来“应该能走索引”。很多慢查就卡在这类细节上。

text=ZqhQzanResources