索引生效及失效场景总结

在使用数据库的时候,常常使用索引来加快数据的检索速度,提高数据的查找性能。但是,在使用索引的时候,要明确哪些场景下索引会生效,哪些场景下索引会失效。应尽可能避免索引失效,尽量保证索引生效。

这里基于MySQL总结出以下索引生效及失效场景总结,对于其他类型的数据库应参考使用,不应直接拿来就用。

如何判断索引是否生效

在执行查询的时候,如果判断一个sql语句是否使用到了索引,可以通过该sql的"执行计划"来判断。关于MySQL执行计划的文章可以参考这篇WIKI。本文重点介绍下索引生效和失效的场景。

索引生效场景

索引在sql中使用。如where子句、order by子句、join子句、select子句等。

(1) where子句

查询引擎会根据where子句中涉及的字段优先选择索引查询数据。

(2) order by子句

当使用order by 将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果) ,这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更不要说读到内存之后的排序了。

但是如果我们对该字段建立索引后,那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果是分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)

(3) join子句

join语句匹配关系(on)涉及的字段建立索引能够提高效率。

(4) select子句

在执行select 子句中,如果select中的查询字段存在于覆盖索引中,那么无需读取记录即可返回。所以在编写select子句时,尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。

索引失效场景

在使用索引的时候,要注意索引失效的场景,尽量避免索引失效的场景。

(1) like语句以%开头,会导致索引失效

模糊查询时,使用%且将其放在开头,会导致查询优化器不得不使用全表查询,从而导致索引失效。如果是"XXX%",则可以正常使用索引。所以在基于like实现模糊匹配的时候,优先保证前置匹配模糊查询。

如果需要实现完全模糊匹配,需要查询的数据规模可控,否则会引入性能问题。

举例来说,有表custom_table,且custom_column_1列声明为索引,那么 "select * from custom_table where custom_column_1 like '%test';"语句执行时,会触发全表扫描,而"select * from custom_table where custom_column_1 like 'test%';"可以使用到索引。

(2) 索引列参与计算,会导致索引失效(如执行算数运算或使用函数或存在类型转换)

当索引列参与计算时,因为存在中间值,所以会导致索引失效。常见的计算场景有类型转换算数运算使用函数 等场景。

举例来说,有表custom_table,且custom_column_1列是字符串类型,并声明为索引,那么 "select * from custom_table where custom_column_1 = 12346;"语句执行时,会触发全表扫描,而"select * from custom_table where custom_column_1 = '123456';"可以使用到索引。这是因为字符串的索引字段在查询时,如果数据没有使用引号,会进行类型转换,如果待转换数据不匹配。

(3) 查询条件中有or,如果存在or相关的字段没有索引,会导致语句索引失效

如果查询条件中有or,需要确保or相关的字段都要有索引,否则会导致索引失效。举例来说,有表custom_table,且custom_column_1列设置了索引,custom_column_2列是普通列。那么"select * from custom_table where custom_column_1 = 'test' or custom_column_2 = 'foo';"语句执行时,会触发全表扫描,因为custom_column_2列没有设置索引。

(4) 违背最左匹配原则,会导致索引失效

如果是一个多码索引(也称联合索引、组合索引),其索引匹配遵循最左匹配规则,如果违背会导致索引失效。举例来说,如果声明(A,B,C)的多码索引,那么数据库会同时际建立了(A)、(A,B)、(A,B,C)三个索引,如果只基于字段B查询,是无法触发索引的。

(5) 反向查询可能不会使用索引(如not in、not exist)

如果在查询的时候,使用了反向查询相关的语句,要注意确认下索引是否生效。这里以not in 为例,简单介绍下反向查询场景下索引失效的情况。如果select的字段都是索引字段,则not in也会触发索引,如果存在至少一个字段没有索引,则会触发全表扫描。

举例来说,有表custom_table,custom_column_1列声明为索引,custom_column_2列是普通列。执行"select custom_column_1, custom_column_2 from custom_table where custom_column_1 not in ('test1', 'test2');"语句时,会触发全表扫描,而"select custom_column_1 from custom_table where custom_column_1 not in ('test1', 'test2');"可以使用到索引。

参考

https://blog.csdn.net/solihawk/article/details/120756584 数据库系列之MySQL中的执行计划
https://dev.mysql.com/doc/refman/8.0/en/verifying-index-usage.html Verifying Index Usage
https://blog.csdn.net/USTC_Zn/article/details/94356505 数据库使用规范(索引规范,SQL规范,表设计规范等)
https://blog.csdn.net/sy_white/article/details/122112440 索引失效的情况及解决(超详细)
https://zhuanlan.zhihu.com/p/339441666 超全的数据库建表、SQL、索引规范
https://blog.csdn.net/Wu_Shang001/article/details/107607627 关于 mysql not in 是否走索引的问题, mysql版本5.7
https://juejin.cn/post/7069562982711164965 15个必知的Mysql索引失效场景
https://programmerall.com/article/87342265154/ Common scene and circumvention method for index failure in mysql

相关推荐
Hello.Reader27 分钟前
StarRocks实时分析数据库的基础与应用
大数据·数据库
执键行天涯29 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
yanglamei196239 分钟前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
工作中的程序员1 小时前
ES 索引或索引模板
大数据·数据库·elasticsearch
严格格1 小时前
三范式,面试重点
数据库·面试·职场和发展
微刻时光1 小时前
Redis集群知识及实战
数据库·redis·笔记·学习·程序人生·缓存
单字叶2 小时前
MySQL数据库
数据库·mysql
mqiqe2 小时前
PostgreSQL 基础操作
数据库·postgresql·oracle
just-julie2 小时前
MySQL面试题——第一篇
数据库·mysql
趋势大仙2 小时前
SQLiteDatabase insert or replace数据不生效
android·数据库