🗣 Wd提问: MySQL索引失效场景
🤖 Ai回答: MySQL索引失效是指在查询语句中,虽然表上存在索引,但MySQL优化器并未使用该索引,而是选择了全表扫描或其他代价更高的执行方式。这会导致查询性能下降。以下是常见的MySQL索引失效场景,按原因分类整理:
一、WHERE条件中使用了函数或表达式
❌ 失效示例:
sql
SELECT * FROM users WHERE YEAR(create_time) = 2023;
或
SELECT * FROM users WHERE UPPER(name) = 'ZHAO';
✅ 解决方案:
避免在索引列上使用函数或表达式。
可以考虑建立函数索引(MySQL 8.0+支持)或调整查询逻辑。
使用范围查询代替函数计算(如:`create_time = '2023-01-01' AND create_time < '2024-01-01'`)
二、使用了不等于(!=)、<、NOT IN、NOT EXISTS 等操作符
❌ 失效示例:
sql
SELECT * FROM users WHERE status != 1;
SELECT * FROM users WHERE id NOT IN (1, 2, 3);
✅ 解决方案:
对于 `NOT IN`,可改用 `IS NULL` 或子查询 + `EXISTS`。
对于 `!=`,如果数据分布均匀,可能仍能使用索引;但如果过滤比例大(如 30%),优化器可能放弃索引。
考虑是否可以改写为 `IN` 或 `BETWEEN`。
⚠️ 注意:MySQL对 `!=` 和 `NOT IN` 的索引选择性评估较保守,容易导致索引失效。
三、模糊查询使用左通配符(%开头)
❌ 失效示例:
sql
SELECT * FROM users WHERE name LIKE '%张%';
或
SELECT * FROM users WHERE name LIKE '%张';
✅ 解决方案:
尽量避免左通配符,改为右通配符或中间匹配(如 `LIKE '张%'` 是有效的)。
如果必须左模糊,可考虑全文索引(FULLTEXT)或ElasticSearch等外部搜索系统。
四、字段类型不一致(隐式转换)
❌ 失效示例:
sql
假设name是VARCHAR,但传入的是数字
SELECT * FROM users WHERE name = 123; 字符串与数字比较
或者:
SELECT * FROM users WHERE phone = '13800138000'; phone是INT类型
✅ 解决方案:
明确类型转换,确保查询值与字段类型一致。
在应用层做好类型校验,避免隐式转换。
五、复合索引未遵循最左前缀原则
❌ 失效示例:
sql
CREATE INDEX idx_name_age ON users(name, age);
下面查询无法使用索引:
SELECT * FROM users WHERE age = 25; 只用了age,跳过了name
SELECT * FROM users WHERE name = '张三' AND city = '北京'; city无索引,且未包含在索引中
✅ 解决方案:
复合索引必须从左到右连续使用。
查询条件应尽量包含索引的前导列。
如果经常按 `city` 查询,可考虑重新设计索引顺序(如 `(city, name, age)`)。
六、OR 条件导致索引失效
❌ 失效示例:
sql
SELECT * FROM users WHERE name = '张三' OR age = 25;
✅ 解决方案:
用 `UNION` 替代 `OR`:
sql
SELECT * FROM users WHERE name = '张三'
UNION
SELECT * FROM users WHERE age = 25;
或者检查是否能合并成一个复合索引。
⚠️ MySQL 5.7+ 中对 OR 有部分优化,但依然不稳定,建议优先使用 `UNION`。
七、LIKE ‘%xxx’ 形式(前面带通配符)
见第3点,已详述。
八、使用了NULL值判断(IS NULL / IS NOT NULL)
❌ 失效示例:
sql
SELECT * FROM users WHERE email IS NULL;
✅ 解决方案:
索引对 `IS NULL` 有时能用,但效果取决于数据分布和统计信息。
如果字段允许 `NULL` 且查询频繁,建议在设计时考虑是否允许 `NULL`,或增加默认值。
对于大量 `NULL` 数据,索引效率低,可考虑用其他策略(如分表、分区)。
九、数据量小或选择性差(索引无意义)
❌ 失效示例:
sql
SELECT * FROM users WHERE status = 1; status只有0和1,且1占90%
✅ 解决方案:
当筛选比例过高(如 30%),MySQL优化器认为走索引不如全表扫描。
此时即使有索引,也可能被忽略。
可以结合其他列组成复合索引提升选择性。
十、索引字段参与运算或函数
❌ 失效示例:
sql
SELECT * FROM users WHERE id + 1 = 100; id+1不能用索引
SELECT * FROM users WHERE DATE(create_time) = '2023-01-01';
✅ 解决方案:
避免在索引列上做任何运算。
改写查询逻辑,如将 `id + 1 = 100` 改为 `id = 99`。
十一、使用了DISTINCT、GROUP BY、ORDER BY 导致索引失效
❌ 失效示例:
sql
SELECT DISTINCT name FROM users ORDER BY age DESC;
如果没有覆盖索引,可能需要临时表排序
✅ 解决方案:
检查是否能通过索引满足 `DISTINCT` / `GROUP BY` / `ORDER BY`。
考虑建立覆盖索引(包含所有查询字段)。
如需排序,确保排序字段在索引中且顺序一致。
十二、使用了临时表或文件排序(filesort)
❌ 场景:
sql
SELECT * FROM users ORDER BY age DESC;
若age无索引,或索引不完整,可能触发 filesort
✅ 解决方案:
为排序字段建立索引。
使用覆盖索引减少回表。
优化SQL结构,避免不必要的排序。
十三、使用了子查询或视图(部分情况)
❌ 失效示例:
sql
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE status = 1);
如果子查询返回大量数据,且外层查询无合适索引,可能导致全表扫描。
✅ 解决方案:
用 `JOIN` 替代子查询:
sql
SELECT u.* FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 1;
确保关联字段有索引。
十四、索引字段被包裹在函数内(再次强调)
如:
sql
SELECT * FROM users WHERE MD5(id) = 'xxx';
这种情况下,索引完全无效。
十五、统计信息过期或不准确
❌ 失效示例:
表刚插入大量数据,未更新统计信息,优化器误判索引价值。
✅ 解决方案:
sql
ANALYZE TABLE users;
手动更新统计信息
✅ 总结:如何避免索引失效?
| 类型 | 建议 |
| 函数/表达式 | 避免在索引列上使用 |
| 左模糊 | 尽量避免 `%xxx`,改用右模糊或全文索引 |
| 复合索引 | 必须遵循最左前缀原则 |
| OR/NOT IN | 用 UNION / EXISTS 替代 |
| 类型不一致 | 明确类型转换 |
| 数据选择性差 | 结合多列建立复合索引 |
| 排序/分组 | 添加对应字段索引,避免 filesort |
| 统计信息 | 定期更新 |
📌 最佳实践建议:
1、使用 EXPLAIN 查看执行计划,确认索引是否被使用。
2、建立合适的复合索引,而非盲目加单列索引。
3、避免在索引列上做计算或函数调用。
4、定期维护统计信息(`ANALYZE TABLE`)。
📣 商家广告:





0
IP地址: 151.28.197.136
搜索次数: 19
提问时间: 2025-11-11 05:17:39
本站所有
❓
问答
均由Ai自动分析整理,内容仅供参考,若有误差请用“联系”里面信息通知我们人工修改或删除。
本站由
🟢
豌豆Ai
提供技术支持,使用的最新版:
豌豆Ai站群搜索引擎系统 V.25.10.25
搭建本站。