什么情况下索引会失效

一、索引创建的原则

1、针对数据量较大,且查询比较频繁的表建立索引。

单表超过10万数据,即可增加索引

2、使用经常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引

3、尽量选择区分度高的字段作为索引,尽量建立唯一索引,区分度越高,使用的索引越高效。

例如下图中的address,区分度太小就不适合作为索引

4、尽量使用联合索引(多个字段),减少单列索引,查询的时候,联合索引很多时候可以用到“覆盖索引”,节省存储空间,避免“回表查询”,提高查询效率

5、索引也不是越多越好,尽量控制索引的数量

索引越多,维护索引结构的代价越大,在增删改的时候效率也越低

6、如果要建立索引的列不能存储null值,请在创建表时,使用not null约束,可以增加查询效率

二、什么情况下索引会失效

首先讲一个我碰到的真实案例:

我们公司系统有一张大表,有80多个字段(按照规范其实不应该有这么多字段,历史遗留问题,后面在数据库国产化的时候,准备优化),每天500万-2000万之间的订单数据;原来是使用日期作为分区键,再走主键索引查询的;

结果有一次代码改动,这张表又加了几个字段,而且在其中一条SQL的select 后加上了几个字段,oracle的索引就既不走分区键,也不走主键索引了,通过查看执行计划发现,是执行到了另外几个字段的普通联合唯一索引上,而我们除了select后加了字段以外,where条件没有改动。

出现的现象:应用更新上去后,立马开始告警,数据库连接超时,告警数据库连接池满。新进来的请求无法下单,大面积异常。决定立马执行回退操作;

回退完成(业务正常)后,开始分析原因:除了加了几个字段以外,select 后加了几个字段要查询出来以外,也没有增加改动索引,也没有where条件的任何改动;后面分析执行计划发现索引走偏了,走到另一个普通索引上了

原因分析为:

Oracle优化器会根据查询的代价(Cost)来选择执行计划。当查询中涉及的字段或数据量发生变化时,优化器可能会重新评估使用不同索引的代价。例如:

  • 新添加的字段可能使得查询结果集的大小或数据分布发生了变化,导致优化器认为使用其他索引更高效。

  • 如果查询涉及的字段在新索引中能够覆盖查询所需的所有列(即覆盖索引),优化器可能会优先选择该索引

当然,在我碰到的这次案例中,oracle优化器,显然是帮了倒忙,选择了错误的索引,导致查询时间超长,占用连接池

解决方案:联系DBA人员,强行绑定执行的索引,使用分区键+主键,问题解决

后续又碰到一次类似的问题,但是我们预料到可能出现这样的情况,提前联系了DBA人员支撑,一出现这个问题,就立马绑定索引;

索引失效的条件:

1、违反最左前缀原则

即:当使用一个联合索引(索引了多列)时,查询条件中,,要从索引的最左前列开始,并且不跳过索引中的列,匹配最左前缀法则,走索引

例如:tb_seller表有一个联合索引,是name、status、address三个字段的联合索引;

上述两条SQL执行,没有带上name字段,所以我们可以看到执行计划中,key和key_len都没有表现出这条SQL走了索引,索引失效

2、范围查询右边的列,不能使用索引

即,如果一个联合索引,某个字段使用了范围查询,那么这个字段后面的索引都不能生效

例如:还是上面这个表,第一条SQL使用了正确的联合索引,执行计划的key显示使用了索引tb_seller_index,索引长度key_len显示612,表示用到了name、status、address三个字段的索引,这三个字段的长度,加起来一共是612

而第二条SQL的条件中,status约束了查询范围“>1”,导致address这个字段的索引没有走到,key_len的长度就只有309了,只使用到了name和status

3、不要在索引列上进行运算操作,索引会失效

即:如果对有索引的列,使用运算操作,那么索引可能会失效

如图,还是这张表,组合索引是(name、status、address)

图中的SQL的执行计划中,key为null,说明没有走到索引,导致索引失效,这样查询会很慢

4、字符串不加单引号,造成索引失效

在查询时,如果没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效

上图中的SQL,第一条正确的SQL的执行计划,能够看到key_len是309,是使用了name、status两个字段的索引;

而第二条SQL的执行计划中,key_len是303,仅仅使用了name这个字段上的索引,造成了status上的索引失效;

原因是:第二条SQL条件中字符串类型的字段status后面的=跟的是0,没有带字符串,MySQL就认为是数字类型,而要与status相匹配,则要转换成字符串类型 ;而进行的类型转换,导致了索引失效

5、以%开头的like模糊查询,索引失效。

但是如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。

如图中,第一条,第二条SQL中,组合索引统统失效,第三条中,%在末尾,并没有导致索引失效

### 三级标题:MySQL索引失效的常见原因和场景 在MySQL中,索引是提升查询性能的重要工具,但在某些特定场景下,即使存在合适的索引,查询也可能无法使用索引,从而导致性能下降。以下是常见的索引失效场景及其原因分析。 **LIKE操作符使用不当** 是导致索引失效的典型原因之一。当使用`LIKE`进行模糊查询时,如果模式以通配符`%`开头,例如`LIKE '%abc'`,MySQL将无法利用相关列上的索引。这是因为索引的结构决定了它只能高效地支持前缀匹配,而不能支持后缀或中间匹配。[^2] **使用否定条件** 也会导致索引失效。具体来说,当查询中包含`!=`、`<>`、`NOT IN`、`NOT LIKE`等否定操作符时,MySQL通常无法使用索引优化查询。例如,创建了一个`idx_status`索引后,查询`SELECT * FROM orders WHERE status != 'completed';`可能不会使用该索引。[^4] **OR连接无索引字段** 是另一个需要注意的情况。如果查询中的`OR`条件中有一部分没有索引支持,那么整个查询可能都无法使用索引。这是因为MySQL优化器在处理`OR`条件时,需要确保所有可能的匹配路径都得到正确的处理。[^3] **联合索引索引不完整** 或者 **最左索引缺失** 同样会导致索引失效。在定义联合索引时,如果查询条件没有包含联合索引中最左侧的列,则该索引可能不会被使用。这是因为联合索引的构建方式类似于字典目录,只有从最左边开始的列才能保证索引的有效性。[^3] 此外,**索引列参与运算** 或 **函数调用** 也会导致索引失效。如果查询条件中对索引列进行了运算或者调用了函数,MySQL通常无法直接使用索引来加速查询。例如,查询`SELECT * FROM table WHERE YEAR(date_column) = 2023;`可能不会使用`date_column`上的索引。 **未用到覆盖索引** 也是常见的问题之一。覆盖索引是指查询的所有列都包含在一个索引中,这样数据库可以直接从索引中获取所需数据,而无需回表查询。如果查询没有使用覆盖索引,即使存在其他相关索引,也可能导致性能下降。 **MySQL放弃使用索引** 的情况也时有发生,尤其是在查询优化过程中,当MySQL估计使用索引的成本高于全表扫描时,它可能会选择不使用索引。这种情况通常发生在表的数据量较小或者统计信息不准确的情况下。 最后,**查询类型错误** 也可能导致索引失效。例如,当查询条件中的数据类型与索引列的数据类型不匹配时,MySQL可能无法正确使用索引。[^3] 综上所述,了解这些索引失效的场景对于优化数据库性能至关重要。通过合理设计查询语句和数据库结构,可以有效避免这些问题,提高查询效率。 ```sql -- 示例:避免索引失效的查询 -- 假设有一个名为orders的表,其中status列上有索引idx_status -- 正确使用索引的方式 SELECT * FROM orders WHERE status = 'pending'; -- 错误使用索引的方式 SELECT * FROM orders WHERE status != 'completed'; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值