在使用数据库的时候,常常使用索引来加快数据的检索速度,提高数据的查找性能。但是,在使用索引的时候,要明确哪些场景下索引会生效,哪些场景下索引会失效。应尽可能避免索引失效,尽量保证索引生效。
这里基于MySQL总结出以下索引生效及失效场景总结,对于其他类型的数据库应参考使用,不应直接拿来就用。
在执行查询的时候,如果判断一个sql语句是否使用到了索引,可以通过该sql的"执行计划"来判断。关于MySQL执行计划的文章可以参考这篇WIKI。本文重点介绍下索引生效和失效的场景。
索引在sql中使用。如where子句、order by子句、join子句、select子句等。
查询引擎会根据where子句中涉及的字段优先选择索引查询数据。
当使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作是很影响性能的,因为需要将查询涉及到的所有数据从磁盘中读到内存(如果单条数据过大或者数据量过多都会降低效率),更不要说读到内存之后的排序了。
但是如果我们对该字段建立索引后,那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果是分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用取出所有数据进行排序再返回某个范围内的数据。(从磁盘取数据是最影响性能的)
对join语句匹配关系(on)涉及的字段建立索引能够提高效率。
在执行select子句中,如果select中的查询字段存在于覆盖索引中,那么无需读取记录即可返回。所以在编写select子句时,尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。
在使用索引的时候,要注意索引失效的场景,尽量避免索引失效的场景。
模糊查询时,使用%且将其放在开头,会导致查询优化器不得不使用全表查询,从而导致索引失效。如果是"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%’;"可以使用到索引。
当索引列参与计算时,因为存在中间值,所以会导致索引失效。常见的计算场景有类型转换、算数运算、使用函数等场景。
举例来说,有表custom_table,且custom_column_1列是字符串类型,并声明为索引,那么 "select * from custom_table where custom_column_1 = 12346;"语句执行时,会触发全表扫描,而"select * from custom_table where custom_column_1 = ‘123456’;"可以使用到索引。这是因为字符串的索引字段在查询时,如果数据没有使用引号,会进行类型转换,如果待转换数据不匹配。
如果查询条件中有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列没有设置索引。
如果是一个多码索引(也称联合索引、组合索引),其索引匹配遵循最左匹配规则,如果违背会导致索引失效。举例来说,如果声明(A,B,C)的多码索引,那么数据库会同时际建立了(A)、(A,B)、(A,B,C)三个索引,如果只基于字段B查询,是无法触发索引的。
如果在查询的时候,使用了反向查询相关的语句,要注意确认下索引是否生效。这里以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