MySQL索引失效的常见情况
索引失效是指查询语句无法有效利用已创建的索引,导致数据库不得不进行全表扫描,这会显著降低查询性能。以下是MySQL中索引失效的主要情况:
一、违反最左前缀原则
对于复合索引(A,B,C),以下情况会导致索引失效:
-- 有效:使用索引(A,B,C)或(A,B)或(A)
SELECT * FROM table WHERE A='a' AND B='b' AND C='c';
SELECT * FROM table WHERE A='a' AND B='b';
SELECT * FROM table WHERE A='a';
-- 失效:没有从最左列开始使用
SELECT * FROM table WHERE B='b' AND C='c';
SELECT * FROM table WHERE C='c';
二、在索引列上使用函数或运算
-- 失效:对索引列使用函数
SELECT * FROM users WHERE YEAR(create_time) = 2023;
SELECT * FROM users WHERE LOWER(name) = 'john';
-- 失效:对索引列进行计算
SELECT * FROM products WHERE price * 2 = 100;
SELECT * FROM employees WHERE salary + 1000 > 5000;
-- 正确写法应保持索引列"干净"
SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
SELECT * FROM users WHERE name = 'john';
SELECT * FROM products WHERE price = 50;
三、使用不等于(!=或<>)
-- 失效:使用不等于操作
SELECT * FROM users WHERE age != 30;
SELECT * FROM orders WHERE status <> 'completed';
四、使用IS NULL或IS NOT NULL
-- 失效:对允许NULL的索引列使用IS NULL/IS NOT NULL
SELECT * FROM customers WHERE phone IS NULL;
SELECT * FROM customers WHERE phone IS NOT NULL;
-- 解决方案:考虑使用默认值代替NULL
五、LIKE以通配符开头
-- 失效:LIKE以%开头
SELECT * FROM products WHERE name LIKE '%apple';
SELECT * FROM products WHERE name LIKE '%apple%';
-- 有效:LIKE不以%开头
SELECT * FROM products WHERE name LIKE 'apple%';
六、数据类型隐式转换
-- 失效:字符串列使用数字查询(隐式类型转换)
SELECT * FROM users WHERE phone = 13800138000; -- phone是varchar类型
-- 正确写法
SELECT * FROM users WHERE phone = '13800138000';
七、使用OR连接条件
-- 失效:OR连接的条件中有一个没有索引
SELECT * FROM users WHERE name = 'John' OR age = 25; -- 假设age没有索引
-- 解决方案1:为所有OR条件列创建索引
-- 解决方案2:改用UNION ALL
SELECT * FROM users WHERE name = 'John'
UNION ALL
SELECT * FROM users WHERE age = 25 AND name != 'John';
八、范围查询后的列无法使用索引
-- 复合索引(A,B,C)
-- A使用范围查询后,B和C无法使用索引
SELECT * FROM table WHERE A > 100 AND B = 'b' AND C = 'c';
-- 解决方案:调整索引顺序或查询条件
九、使用NOT IN或NOT EXISTS
-- 失效:NOT IN通常无法使用索引
SELECT * FROM users WHERE id NOT IN (1, 2, 3);
-- 失效:NOT EXISTS通常无法使用索引
SELECT * FROM orders o WHERE NOT EXISTS (
SELECT 1 FROM payments p WHERE p.order_id = o.id
);
-- 替代方案:考虑使用LEFT JOIN + IS NULL
十、全表扫描更快时
当查询需要返回表中大部分数据(通常超过20-30%)时,优化器可能选择全表扫描而非索引扫描,因为减少随机I/O的开销更大。
如何检测索引失效
-
使用
EXPLAIN
分析查询执行计划:EXPLAIN SELECT * FROM users WHERE name LIKE '%john%';
查看
type
列,如果是ALL
则表示全表扫描 -
使用性能分析工具:
SET profiling = 1; SELECT * FROM users WHERE ...; SHOW PROFILE;
避免索引失效的建议
- 设计合理的复合索引顺序
- 保持查询条件中的索引列"干净"(无函数、计算)
- 避免隐式类型转换
- 对于范围查询,将其放在复合索引的最后
- 考虑使用覆盖索引减少回表操作
- 定期分析表统计信息(
ANALYZE TABLE
)
理解这些索引失效的场景,可以帮助你编写更高效的SQL查询,并设计更合理的数据库索引结构。