Mysql避免索引失效
1. 在索引列上使用函数或表达式
问题描述
SELECT * FROM users WHERE YEAR(create_time) = 2023;
如果create_time
列上有索引,上述查询会导致索引失效,因为MySQL无法直接利用索引的B+树结构。
解决方法
将函数应用于条件值,而不是列:
SELECT * FROM users WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01';
2. 使用类型隐式转换
问题描述
SELECT * FROM users WHERE phone = 13800138000;
如果phone
列是VARCHAR
类型,而条件值是整数,MySQL会进行隐式类型转换,导致索引失效。
解决方法
确保条件值与索引列类型一致:
SELECT * FROM users WHERE phone = '13800138000';
3. 使用不等于或不包含操作符
问题描述
SELECT * FROM users WHERE name != 'John';
使用!=
、<>
、NOT IN
、NOT LIKE
等否定条件时,通常会导致索引失效。
解决方法
尽量使用肯定条件替代否定条件。如果必须使用否定条件,可以考虑将查询拆分为多个子查询。
4. 使用OR
操作符连接不同索引列
问题描述
SELECT * FROM users WHERE name = 'John' OR email = 'john@example.com';
如果name
和email
列上有不同的索引,上述查询可能导致索引失效。
解决方法
使用UNION
替代OR
:
SELECT * FROM users WHERE name = 'John'
UNION
SELECT * FROM users WHERE email = 'john@example.com';
或者创建复合索引(如果适用)。
5. 使用LIKE
操作符且以通配符开头
问题描述
SELECT * FROM products WHERE product_name LIKE '%phone%';
以通配符%
开头的LIKE
查询会导致索引失效。
解决方法
-
尽量使用后缀通配符:
SELECT * FROM products WHERE product_name LIKE 'phone%';
-
如果必须使用前缀通配符,可以考虑使用全文索引。
6. 对索引列进行运算
问题描述
SELECT * FROM products WHERE price + 100 > 500;
对索引列进行运算会导致索引失效。
解决方法
将运算应用于条件值,而不是列:
SELECT * FROM products WHERE price > 400;
7. 查询条件中的字段顺序与复合索引顺序不一致
问题描述
SELECT * FROM users WHERE age = 25 AND city = 'New York' AND name = 'John';
如果复合索引是(name, age, city)
,上述查询可能无法充分利用索引。
解决方法
保持查询条件顺序与索引列顺序一致:
SELECT * FROM users WHERE name = 'John' AND age = 25 AND city = 'New York';
8. 使用不等值范围查询
问题描述
SELECT * FROM employees WHERE age > 30 AND salary > 50000;
如果复合索引是(age, salary)
,salary
部分无法使用索引。
解决方法
调整索引顺序,将范围查询的列放在最后:
CREATE INDEX idx_salary_age ON employees(salary, age);
9. ORDER BY
或GROUP BY
子句使用不当
问题描述
SELECT * FROM orders ORDER BY order_date DESC;
如果order_date
列上有索引,但查询条件中没有使用该列,可能导致额外的排序操作。
解决方法
确保ORDER BY
或GROUP BY
子句中的字段与WHERE条件中使用的索引列一致,或者创建覆盖索引:
CREATE INDEX idx_order_date_status ON orders(order_date, order_status);
10. 查询的数据占表中数据的比例较大
问题描述
SELECT * FROM orders WHERE order_status = 'shipped';
如果order_status
字段的选择性很低(例如只有两个值),MySQL优化器可能会选择全表扫描。
解决方法
-
增加更多的过滤条件,减小结果集。
-
使用覆盖索引避免回表查询。
11. 使用IS NULL
或IS NOT NULL
问题描述
SELECT * FROM users WHERE phone IS NULL;
如果phone
列上有索引,IS NULL
或IS NOT NULL
查询可能会导致索引失效。
解决方法
确保索引列允许NULL
值,并且优化器能够正确利用索引。如果NULL
值较多,可以考虑使用额外的字段或标志位。
12. 使用IN
操作符
问题描述
SELECT * FROM users WHERE id IN (1, 2, 3);
如果IN
操作符中的值较多,可能导致索引失效。
解决方法
-
如果
IN
操作符中的值较少,可以使用UNION
替代:SELECT * FROM users WHERE id = 1 UNION SELECT * FROM users WHERE id = 2 UNION SELECT * FROM users WHERE id = 3;
-
如果
IN
操作符中的值较多,可以考虑将这些值存储在临时表中,然后通过JOIN
操作查询。
13. 使用DISTINCT
问题描述
SELECT DISTINCT name FROM users WHERE age = 25;
如果name
列上没有索引,DISTINCT
操作可能导致性能问题。
解决方法
为name
列创建索引,或者使用GROUP BY
替代DISTINCT
:
SELECT name FROM users WHERE age = 25 GROUP BY name;
14. 使用LIMIT
问题描述
SELECT * FROM orders ORDER BY order_date DESC LIMIT 10;
如果order_date
列上有索引,但查询条件中没有使用该列,可能导致额外的排序操作。
解决方法
确保ORDER BY
子句中的字段与WHERE条件中使用的索引列一致,或者创建覆盖索引:
CREATE INDEX idx_order_date_status ON orders(order_date, order_status);
总结
避免索引失效的关键在于:
-
遵循最左匹配原则。
-
避免在索引列上使用函数或表达式。
-
确保查询条件、排序和分组字段与索引列一致。
-
使用覆盖索引避免回表查询。
-
避免使用否定条件、
OR
操作符和前缀通配符。 -
定期分析和优化索引。
通过这些方法,可以有效避免索引失效,提升MySQL查询性能。