SQL索引失效情况

文章详细介绍了MySQL中索引可能失效的情况,包括在索引列上进行运算导致失效,字符串不加引号引起的隐式类型转换,头部模糊匹配,OR条件分隔的未索引字段,以及数据分布影响下的索引使用决策。同时提到了ISNULL和ISNOTNULL操作在特定情况下是否使用索引取决于数据分布。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

索引失效情况

首先准备tb_user表如下:
在这里插入图片描述
其中tb_user的索引情况如下:

在这里插入图片描述

1.索引列运算

在索引列上进行运算操作,索引将失效。

由上面索引表知还有一个索引,是phone字段的单列索引
在这里插入图片描述

A. 当根据phone字段进行等值匹配查询时, 索引生效。

   explain select * from tb_user where phone = '17799990015';

在这里插入图片描述
B. 当根据phone字段进行函数运算操作之后,索引失效。

explain select * from tb_user where substring(phone,10,2) = '15';

在这里插入图片描述

2.字符串不加引号

字符串类型字段使用时,不加引号,索引将失效。
接下来,我们通过两组示例,来看看对于字符串类型的字段,加单引号与不加单引号的区别:

explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';
explain select * from tb_user where profession = '软件工程' and age = 31 and status = 0;

在这里插入图片描述

explain select * from tb_user where phone = '17799990015';
explain select * from tb_user where phone = 17799990015;

在这里插入图片描述
经过上面两组示例,我们会明显的发现,如果字符串不加单引号,对于查询结果,没什么影响,但是数
据库存在隐式类型转换,索引将失效。

3.模糊查询

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
接下来,我们来看一下这三条SQL语句的执行效果,查看一下其执行计划:
由于下面查询语句中,都是根据profession字段查询,符合最左前缀法则,联合索引是可以生效的,
我们主要看一下,模糊查询时,%加在关键字之前,和加在关键字之后的影响。

explain select * from tb_user where profession like '软件%';
explain select * from tb_user where profession like '%工程';
explain select * from tb_user where profession like '%工%';

在这里插入图片描述
经过上述的测试,我们发现,在like模糊查询中,在关键字后面加%,索引可以生效。而如果在关键字
前面加了%,索引将会失效。

4.or连接条件

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会
被用到

explain select * from tb_user where id = 10 or age = 23;
explain select * from tb_user where phone = '17799990017' or age = 23;

在这里插入图片描述
由于age没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对于age也要建立索引。
然后,我们可以对age字段建立索引

create index idx_user_age on tb_user(age);

在这里插入图片描述
建立了索引之后,我们再次执行上述的SQL语句,看看前后执行计划的变化。
在这里插入图片描述
最终,我们发现,当or连接的条件,左右两侧字段都有索引时,索引才会生效。
## 5.数据分布影响
如果MySQL评估使用索引比全表更慢,则不使用索引。

explain select * from tb_user where phone >= '17799990005';
explain select * from tb_user where phone >= '17799990015';

在这里插入图片描述
经过测试我们发现,相同的SQL语句,只是传入的字段值不同,最终的执行计划也完全不一样,这是为什么呢?
就是因为MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。

接下来,我们再来看看 is null 与 is not null 操作是否走索引。
执行如下两条语句 :

explain select * from tb_user where profession is null;
explain select * from tb_user where profession is not null;

在这里插入图片描述
接下来,我们做一个操作将profession字段值全部更新为null。

update tb_user set profession = null;

在这里插入图片描述
然后,再次执行上述的两条SQL,查看SQL语句的执行计划。
在这里插入图片描述

最终我们看到,一模一样的SQL语句,先后执行了两次,结果查询计划是不一样的,为什么会出现这种现象,这是和数据库的数据分布有关系。查询时MySQL会评估,走索引快,还是全表扫描快,如果全表扫描更快,则放弃索引走全表扫描。 因此,is null 、is not null是否走索引,得具体情况具体
分析,并不是固定的。

### SQL索引失效的原因及优化方法 SQL索引失效通常发生在某些特定的查询条件或操作中,这些情况会导致数据库引擎无法利用已有的索引,从而影响查询性能。以下是常见的索引失效原因及对应的优化方法: #### 1. 索引失效的原因 - **使用函数或表达式**:当在索引列上使用函数或表达式时,索引可能失效。例如: ```sql SELECT * FROM employees WHERE YEAR(birth_date) = 1980; ``` 这种情况下,`birth_date` 列上的索引将无法被使用[^3]。 - **不等于条件 (`!=` 或 `<>`)**:使用 `!=` 或 `<>` 条件可能导致索引失效,因为数据库引擎需要扫描更多数据来满足条件。 ```sql SELECT * FROM employees WHERE department_id != 3; ``` - **`NOT IN` 子查询**:`NOT IN` 操作符可能导致索引失效,尤其是在子查询返回空值时[^3]。 - **模糊查询前缀通配符**:如果在 `LIKE` 查询中使用前缀通配符(如 `%abc`),则索引可能失效,因为数据库需要扫描整个索引树。 ```sql SELECT * FROM employees WHERE name LIKE '%John'; ``` - **数据类型不匹配**:如果查询条件中的数据类型与索引列的数据类型不匹配,可能会导致索引失效。例如: ```sql SELECT * FROM employees WHERE salary = '5000'; -- salary 是数值类型 ``` - **隐式类型转换**:当数据库进行隐式类型转换时,也可能导致索引失效。例如: ```sql SELECT * FROM employees WHERE hire_date = 20230101; -- hire_date 是日期类型 ``` #### 2. SQL性能优化方法 - **避免在索引列上使用函数或表达式**:尽量避免对索引列应用函数或表达式,确保查询条件直接作用于索引列。例如,可以改写上述 `YEAR()` 示例为: ```sql SELECT * FROM employees WHERE birth_date >= '1980-01-01' AND birth_date < '1981-01-01'; ``` - **使用 `EXPLAIN` 分析执行计划**:通过 `EXPLAIN` 查看查询的执行计划,确认是否正确使用了索引。重点关注以下字段: - `type`:理想情况下应为 `ref` 或 `eq_ref`,而不是 `ALL`。 - `rows`:表示扫描的行数,越少越好。 - `Extra`:避免出现 `Using temporary` 或 `Using filesort`,这通常意味着需要优化查询[^2]。 - **优化查询条件**:尽量使用精确匹配(`=`)而非范围匹配(`>`、`<`、`BETWEEN`),并确保数据类型一致。例如: ```sql SELECT * FROM employees WHERE department_id = 3; ``` - **合理选择索引**:根据查询需求创建合适的索引,如复合索引、覆盖索引等。例如,对于频繁使用的多列查询,可以创建复合索引: ```sql CREATE INDEX idx_employee_department_salary ON employees(department_id, salary); ``` - **避免不必要的列选择**:仅选择所需的列,减少数据传输量和内存消耗。例如: ```sql SELECT id, name FROM employees WHERE department_id = 3; ``` - **定期分析和维护索引**:使用 `ANALYZE TABLE` 更新表的统计信息,确保查询优化器能够做出正确的决策[^4]。 ```python # 示例:Python脚本生成慢查询日志分析 import mysql.connector def analyze_slow_queries(host, user, password, database): connection = mysql.connector.connect( host=host, user=user, password=password, database=database ) cursor = connection.cursor() cursor.execute("SHOW PROCESSLIST;") processes = cursor.fetchall() for process in processes: if process[5] > 5: # 查询时间超过5秒 print(f"Slow Query: {process[7]}") cursor.close() connection.close() analyze_slow_queries("localhost", "root", "password", "test_db") ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值