索引失效的五大关键场景

MySQL索引失效的条件和原因分析

索引失效概述

MySQL索引失效是指数据库查询时,虽然存在合适的索引,但优化器决定不使用索引而选择全表扫描的情况。索引失效会导致查询性能急剧下降,特别是在数据量较大的表中,性能差异可能达到几个数量级。

主要索引失效场景

1. 数据类型不匹配

当查询条件中的数据类型与索引列定义的数据类型不一致时,MySQL需要进行隐式类型转换,这会导致索引失效。

sql 复制代码
-- 示例:phone字段为varchar类型,但查询使用数字
CREATE INDEX idx_phone ON users(phone);
-- 索引失效的查询
SELECT * FROM users WHERE phone = 123456789; -- 数字与varchar比较
-- 正确的查询
SELECT * FROM users WHERE phone = '123456789'; -- 字符串比较

在这种情况下,MySQL需要将phone列的值转换为数字进行比较,无法使用索引。

2. 对索引列使用函数或表达式

在WHERE条件中对索引列使用函数、计算或表达式时,索引通常无法使用。

sql 复制代码
-- 示例:对日期字段使用函数
CREATE INDEX idx_birthday ON users(birthday);
-- 索引失效的查询
SELECT * FROM users WHERE YEAR(birthday) = 1990;
SELECT * FROM users WHERE birthday + INTERVAL 1 DAY > '2023-01-01';
-- 正确的查询方式
SELECT * FROM users WHERE birthday BETWEEN '1990-01-01' AND '1990-12-31';

任何对索引列的函数包装都会使优化器无法直接使用索引。

3. LIKE模糊查询使用不当

LIKE操作符在特定模式下会导致索引失效:

sql 复制代码
CREATE INDEX idx_name ON users(name);
-- 索引失效的查询(前导通配符)
SELECT * FROM users WHERE name LIKE '%张三%';
SELECT * FROM users WHERE name LIKE '%张三';
-- 可以使用索引的查询
SELECT * FROM users WHERE name LIKE '张三%';

只有右模糊查询('value%')可以使用索引,左模糊('%value')和全模糊('%value%')都会导致索引失效。

4. OR连接条件处理不当

当OR连接的条件中有一个列没有索引时,整个查询可能无法使用索引:

sql 复制代码
CREATE INDEX idx_age ON users(age);
-- 索引失效的查询
SELECT * FROM users WHERE age > 18 OR name = '张三';
-- 优化的查询方式
SELECT * FROM users WHERE age > 18 
UNION 
SELECT * FROM users WHERE name = '张三';

OR条件要求所有涉及的列都有合适的索引才能使用索引。

5. 联合索引违反最左前缀原则

联合索引的使用必须遵循最左前缀匹配原则:

索引定义 有效查询条件 失效查询条件
idx_name_age (name, age) WHERE name='张三' WHERE age=25
idx_name_age (name, age) WHERE name='张三' AND age=25 WHERE age=25 AND name='张三'
idx_name_age (name, age) WHERE name LIKE '张%' WHERE age>20
sql 复制代码
CREATE INDEX idx_name_age ON users(name, age);
-- 可以使用索引的查询
SELECT * FROM users WHERE name = '张三';
SELECT * FROM users WHERE name = '张三' AND age = 25;
-- 索引失效的查询
SELECT * FROM users WHERE age = 25;

联合索引类似于电话簿的排序方式,必须从最左边的列开始使用。

6. 范围查询后的列索引失效

在联合索引中,范围查询会使后续的索引列失效:

sql 复制代码
CREATE INDEX idx_age_salary ON users(age, salary);
-- 索引部分失效的查询
SELECT * FROM users WHERE age > 25 AND salary > 5000;
-- 优化后的查询
SELECT * FROM users WHERE age = 26 AND salary > 5000
UNION ALL
SELECT * FROM users WHERE age = 27 AND salary > 5000
-- 可以继续联合其他age值...

在这个例子中,只有age列能使用索引,salary列无法使用索引进行范围扫描。

7. 使用不等于操作符

不等于操作符(!=, <>)通常会导致索引失效:

sql 复制代码
CREATE INDEX idx_status ON orders(status);
-- 索引失效的查询
SELECT * FROM orders WHERE status != 'completed';
-- 优化的查询方式
SELECT * FROM orders WHERE status IN ('pending', 'processing', 'cancelled');

不等于操作需要检查所有不满足条件的值,这通常会导致全表扫描。

8. IS NULL和IS NOT NULL条件

在某些情况下,IS NULL条件可能导致索引失效:

sql 复制代码
CREATE INDEX idx_email ON users(email);
-- 可能索引失效的查询
SELECT * FROM users WHERE email IS NULL;
-- 如果NULL值较多时的优化
SELECT * FROM users WHERE email = '' OR email IS NULL;

是否使用索引取决于表中NULL值的分布比例。

9. IN操作符的使用

虽然IN操作符通常可以使用索引,但在某些情况下可能失效:

sql 复制代码
CREATE INDEX idx_category ON products(category);
-- 可能索引失效的情况(IN列表过长)
SELECT * FROM products WHERE category IN ('cat1','cat2',...,'cat100');
-- 优化:拆分为多个查询或使用临时表

当IN列表中的值过多时,优化器可能认为全表扫描更高效。

索引失效的排查方法

使用EXPLAIN分析查询

sql 复制代码
EXPLAIN SELECT * FROM users WHERE name LIKE '%张三%';

分析EXPLAIN结果的关键字段:

字段 理想值 说明
type const, ref, range 表示索引使用类型
key 索引名称 实际使用的索引
key_len 索引长度 使用的索引字节数
rows 较小值 预估扫描行数
Extra Using index 表示使用覆盖索引

具体排查步骤

  1. 检查查询条件:确认WHERE条件是否涉及索引列的函数操作
  2. 验证数据类型:确保比较条件的数据类型匹配
  3. 分析联合索引:检查是否遵循最左前缀原则
  4. 评估数据分布:使用SHOW INDEX FROM table_name分析索引统计信息
  5. 测试不同写法:尝试重写查询逻辑,比较执行计划

优化建议

1. 查询重写技巧

sql 复制代码
-- 原始查询(可能索引失效)
SELECT * FROM logs WHERE DATE(create_time) = '2023-01-01';
-- 优化后的查询
SELECT * FROM logs WHERE create_time >= '2023-01-01 00:00:00' 
                    AND create_time < '2023-01-02 00:00:00';

2. 索引设计策略

  • 为高频查询条件创建合适的单列索引
  • 设计联合索引时,将选择性高的列放在前面
  • 考虑覆盖索引,避免回表操作
  • 定期分析索引使用情况,删除冗余索引

3. 数据库配置优化

  • 设置合适的innodb_stats_sample_pages提高统计准确性
  • 定期执行ANALYZE TABLE更新统计信息
  • 监控slow_query_log识别性能问题

通过理解这些索引失效的场景和掌握相应的排查方法,可以显著提升MySQL数据库的查询性能,避免不必要的全表扫描。


参考来源

相关推荐
殷紫川6 分钟前
MySQL 锁等待与死锁根治全攻略:从底层原理到 innodb status 精准定位实战
mysql
MaCa .BaKa15 分钟前
44-校园二手交易系统(小程序)
java·spring boot·mysql·小程序·maven·intellij-idea·mybatis
无小道1 小时前
Mysql——索引
mysql·索引·搜索
元宝骑士1 小时前
深度解析 ROW_NUMBER() 窗口函数:从入门到实战避坑指南
后端·mysql
014-code2 小时前
MySQL 常用业务 SQL
数据库·sql·mysql
y = xⁿ2 小时前
【MySQL】数据库的脏读,不可重复读和幻读,覆盖索引是什么,索引类型有哪些
数据库·mysql
羊小蜜.3 小时前
Mysql 07: 正则表达式查询(REGEXP)全解
数据库·mysql·正则表达式
Dxy12393102163 小时前
正则表达式如何匹配提取文章日期
数据库·mysql·正则表达式
元宝骑士3 小时前
MySQL联表查询优化实战:小表驱动大表的联合索引设计
后端·mysql
前进的李工3 小时前
MySQL用户管理与权限控制指南(含底层架构说明)
开发语言·数据库·sql·mysql·架构