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 NULL
或 IS NOT NULL
如果索引列允许 NULL
值,IS NULL
或 IS NOT NULL
查询可能导致索引失效。
尽管新版本的 MySQL 优化器对此有所改进,但仍需注意。
解决方案:
在创建表时,如果该列不需要 NULL
值,尽量将其定义为 NOT NULL
,并设置默认值。
避免在 WHERE
子句中使用 IS NULL
或 IS NOT NULL
。
7. 使用 ORDER BY
或 GROUP BY
的列与索引不一致
当 ORDER BY
或 GROUP BY
的列与索引的顺序不一致,或者对这些列进行了计算,都可能导致索引失效,无法使用索引的排序功能。
错误示例: 联合索引 (a, b)
-- 排序顺序与索引不一致,索引失效
SELECT * FROM table ORDER BY b, a;
解决方案:
保持一致: 确保 ORDER BY
和 GROUP BY
的列顺序与索引列顺序一致。
避免计算: ORDER BY
的列不要进行计算或使用函数。
8. 优化ORDER BY子句
确保ORDER BY
的列与索引的顺序和列完全一致,或者与查询的WHERE
条件一起构成联合索引的最左前缀。