8大MySQL索引失效解决方案

MySQL是大型架构的必备技能,下面我重点详解8大MySQL索引失效解决方案@mikechen

1. 违反最左前缀原则

当使用联合索引时,如果查询条件没有从索引的最左列开始,则索引会失效。

错误示例: 联合索引 (a, b, c)

-- 索引只使用了 b,a 缺失,索引失效
SELECT * FROM table WHERE b = 1 AND c = 2;

-- 索引只使用了 a 和 c,b 缺失,索引部分失效
SELECT * FROM table WHERE a = 1 AND c = 2;

解决方案: 确保查询条件从联合索引的最左列开始,并且连续使用。

-- 索引完全生效
SELECT * FROM table WHERE a = 1 AND b = 2 AND c = 3;

 

2. 注意隐式类型转换

确保查询条件中的数据类型与索引列的数据类型严格一致。

例如,如果索引列是字符串类型,查询时也要用字符串进行比较。

 

3. 使用 LIKE 查询时以 % 开头

使用 LIKE '...%'(前缀匹配)时,索引有效。

但使用 LIKE '%...'LIKE '%...%'(后缀或全模糊匹配)时,索引失效。

错误示例:

-- 后缀匹配,索引失效
SELECT * FROM table WHERE name LIKE '%John';

解决方案:

如果业务允许,尽量使用 LIKE 'John%'、或引入 ElasticSearch 等搜索引擎。

 

4. 使用 OR 连接条件

如果 OR 连接的两个条件中,其中一个列没有索引,那么整个查询都会走全表扫描。

即使两个列都有索引,有时也可能因为优化器选择全表扫描更优而失效。

错误示例:

-- name 有索引,age 没有索引,索引失效
SELECT * FROM user WHERE name = 'John' OR age = 20;

解决方案:

为所有 OR 条件的列都创建索引。

改写为 UNION: 将 OR 查询拆分为多个 SELECT 语句,再用 UNION 连接起来。

SELECT * FROM user WHERE name = 'John'
UNION
SELECT * FROM user WHERE age = 20;

 

5. 使用 NOT IN!=

NOT IN!= (不等于) 操作符,通常会导致数据库进行全表扫描。

错误示例:

SELECT * FROM user WHERE status != 'active';

解决方案:

如果可以,将 != 转换为 >< 等范围查询。

对于 NOT IN,可以考虑使用 LEFT JOIN ... WHERE ... IS NULL 的方式进行改写,通常性能更优。

 

6. 索引列使用 IS NULLIS NOT NULL

如果索引列允许 NULL 值,IS NULLIS NOT NULL 查询可能导致索引失效。

尽管新版本的 MySQL 优化器对此有所改进,但仍需注意。

解决方案:

在创建表时,如果该列不需要 NULL 值,尽量将其定义为 NOT NULL,并设置默认值。

避免在 WHERE 子句中使用 IS NULLIS NOT NULL

 

7. 使用 ORDER BYGROUP BY 的列与索引不一致

ORDER BYGROUP BY 的列与索引的顺序不一致,或者对这些列进行了计算,都可能导致索引失效,无法使用索引的排序功能。

错误示例: 联合索引 (a, b)

-- 排序顺序与索引不一致,索引失效
SELECT * FROM table ORDER BY b, a;

解决方案:

保持一致: 确保 ORDER BYGROUP BY 的列顺序与索引列顺序一致。

避免计算: ORDER BY 的列不要进行计算或使用函数。

 

8. 优化ORDER BY子句

确保ORDER BY的列与索引的顺序和列完全一致,或者与查询的WHERE条件一起构成联合索引的最左前缀。

评论交流
    说说你的看法