索引生效及失效场景总结

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

这里基于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

相关推荐
云和数据.ChenGuang1 小时前
Django 应用安装脚本 – 如何将应用添加到 INSTALLED_APPS 设置中 原创
数据库·django·sqlite
woshilys1 小时前
sql server 查询对象的修改时间
运维·数据库·sqlserver
Hacker_LaoYi1 小时前
SQL注入的那些面试题总结
数据库·sql
建投数据2 小时前
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
数据库·腾讯云
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
你的微笑,乱了夏天4 小时前
linux centos 7 安装 mongodb7
数据库·mongodb
工业甲酰苯胺4 小时前
分布式系统架构:服务容错
数据库·架构
独行soc5 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘