索引失效有哪些?

在工作中,如果我们想要提高一条语句的查询速度,通常都会想对字段建立索引。

但是索引不是万能的。建立了索引,并不意味着任何查询语句都能走索引扫描。

稍不注意,可能查询语句就会导致索引失效,从而走了全表扫描,虽然查询的结果没有问题,但是查询的性能大大降低。


索引存储结构长什么样?

索引的存储结构跟MySQL使用哪种存储引擎有关,因为存储引擎就是负责将数据持久化在磁盘中,而不同的存储引擎采用的所有数据结构也会不相同。

MySQL 默认的存储引擎是 InnoDB ,它采用B+Tree 作为索引的数据结构

在创建表时,InnoDB 存储引擎默认会创建一个主键索引,也就是聚簇索引,其他索引都属于二级索引

MySQL 的 MyISAM 存储引擎支持多种索引数据结构,比如 B+Tree 索引、R树索引、Full-Text索引。

MyISAM 存储引擎在创建表的时候,创建的主键索引默认使用的是 B+Tree索引

虽然,InnoDB 和 MyISAM 都支持 B+Tree 索引,但是它们数据的存储结构实现方式不同。不同之处在于:

  • InnoDB 存储引擎:B+Tree 索引的叶子节点保存数据本身
  • MyISAM 存储引擎:B+Tree 索引的叶子节点保存数据的物理地址

举个例子,展示两种存储引擎的索引存储结构的区别。

这里有一张 t_user 表,其中 id字段为主键索引,其他的都是普通字段。

如果使用的是MyISAM 存储引擎,B+Tree 索引的叶子节点保存数据的物理地址,即用户数据的指针,如下图:

如果使用的是 InnoDB 存储引擎,B+Tree索引的叶子节点保存数据的本身,如下图:(图中叶子节点是单链表,实际上是双链表)

InnoDB 存储引擎根据索引的类型不同,分为聚簇索引和二级索引。它们的区别在于,聚簇索引的叶子节点存放的是实际数据,所有完整的用户数据都存放在聚簇索引的叶子节点上,而二级索引的叶子节点存放的是主键值,而不是实际数据。

如果将 name 字段设置为普通索引,那么这个二级索引如下图所示:(图中为单链表,实际上是双链表)叶子节点仅存放主键值

接下来会举例说明查询过程会怎么选择用哪个索引类型:

在我们使用 [主键索引] 字段作为条件查询的时候,如果要查询的数据都在 [聚簇索引] 的叶子节点里,那么就会在 [聚簇索引] 中的 B+Tree 检索到对应的叶子节点,然后直接读取要查询的数据。如下面这条语句:

select * from t_user where id = 1;

在我们使用 [二级索引] 字段作为条件查询的时候,如果要查询的数据都在 [聚簇索引] 的叶子节点里,那么需要检索两颗 B+Tree :

  • 先在 [二级索引] 的 B+Tree 找到对应的叶子节点,获取主键值
  • 然后用上一步获取的主键值,在 [聚簇索引] 中的 B+Tree 检索到对应的叶子节点,然后获取要查询的数据。

上面这个过程叫做 回表 , 如下面这条语句:

// name 字段为二级索引
select * from t_user where name="林某";

在我们使用 [二级索引] 字段作为条件查询的时候,如果要查询的数据在 [二级索引] 的叶子节点,那么只需要在 [二级索引] 的 B+Tree 找到对应的叶子节点,然后读取要查询的数据,这个过程叫做 覆盖索引,如下面这条查询语句:(覆盖索引省去了回表操作)
 

// name 字段为二级索引
//由于 id 会存在 二级索引的叶子节点,所以不需要进行回表操作->覆盖索引
select id from t_user where name="林某";

上面这些查询语句的条件都用到了索引列,所以在查询过程都用上了索引。

但是并不意味着,查询条件用上了索引列,就查询过程就一定都用上了索引,接下来会例举一些会导致索引失效的情况,而发生全表扫描。


对索引使用左或左右模糊匹配

当我们使用 左或左右模糊匹配的时候,也就是 like %xx like %xx% 的=这两种方式都会造成索引失效

比如下面的 like 语句,查询 name 后缀为 [林] 的用户,执行计划中的 type = ALL 就代表了全表扫描,而没有走索引。

// name 字段为二级索引
select * from t_user where name like '%林';

如果查询的 name 前缀为 林 的用户,那么就会走索引扫描,执行计划中的 type=range 表示走索引扫描 ,key = index_name 表示 实际走了 index_name 索引。

// name 字段为二级索引
select * from t_user where name like '林%';

为什么 like 关键字 左或者 左右 模糊匹配无法走索引呢?

因为索引B+Tree 树是按照 [索引值]有序排列的,只能根据前缀进行比较。

 

eg:下面这张二级索引表,是以 name 字段有序排列顺序的:

假设我们要查询 name 字段前缀为 [林] 的数据,也就是 name like '林%',扫描索引的过程:

  • 首节点查询比较:林这个字的拼音大小比首节点的第一个索引值中的陈字大,但是比首节点的第二个索引值 [周] 字小,所以会选择去 节点2 继续查询;
  • 节点 2 查询比较:节点 2 的第一个索引值中的陈字的拼音大小比林字小,所以继续看下一个索引值,发现节点 2 有与 林 字前缀匹配的索引值,于是就往叶子节点查询,即叶子节点 4 ;
  • 节点 4 查询比较:节点 4 的第一个索引值的前缀符合 林 字,于是就读取该行的数据,接着继续往右匹配,直到匹配不到前缀为 林 的索引值。

 如果使用 name like '%林' 方式来查询,因为查询的结果可能是 [ 陈林,周林...]等之类的,所以不知道从哪个索引值开始比较,于是就只能通过全表扫描的方式来查询。


对索引使用函数

有时候我们会使用一些 MySQL自带的函数来得到我们想要查询的结果,这时,如果查询条件中的索引字段使用函数,就会导致索引失效。

比如下面这条语句的查询条件中对 name 字段使用了 LENGTH 函数,执行计划中的 type = ALL,代表了全表扫描:

// name 为二级索引
select * from t_user where length(name)=6;

不过从 MySQL 8.0 开始,索引特性增加了函数索引,即可以针对函数计算后的值建立一个索引,也就是说该索引的值就是函数计算后的值,所以就可以通过扫描索引来查询数据。

eg:对 length(name) 的计算结果建立一个名为 idx_name_length 的索引

alter table t_user add key idx_name_length ((length(name)))

然后再用下面这条查询语句,这时候就走索引了:


对索引进行表达式计算

在查询条件中对索引进行表达式计算,也是无法走索引的。

比如,下面这条查询语句,执行计划中 type = ALL,说明是通过全表扫描的方式查询数据的:

explain select * from t_user where id + 1 = 10;

但是,如果把查询语句的条件改成 where id = 10 - 1 ,这样就不是在索引字段进行表达式的计算了,于是就可以走索引查询了。


对索引隐式类型转换

如果索引字段是字符串类型,但是在条件查询中,输入的参数是整型的话,在执行计划中会发现这条语句会走全表扫描。

eg:原本的 t_user 表增加了 phone 字段,是 二级索引且类型是 varchar。

然后再条件查询中,用整型作为输入参数,此时执行计划中的 type = ALL,所以是通过全表扫描来查询数据的。

select * from t_user where phone = 1300000001;

但是如果索引字段是整型类型,查询条件中的输入参数即使是字符串,是不会导致索引失效,还是可以走索引扫描的。

 explain select * from t_user where id = '1';

为什么第一个例子会导致索引失效,而第二个不会呢?

要明白这个原因,就需要了解MySQL是会将字符串转换成数字处理,还是将数字转换成字符串处理。

  • 如果规则是 MySQL 会自动将 [字符串] 转换为 [数字],就相当于 select 10 > 9,这个就是数字比较,所以结果应该是 1;
  • 如果规则是 MySQL 会自动将 [数字] 转换为 [字符串] ,就相当于 select "10" > "9",这个是字符串比较,字符串比较大小是逐位从高到低逐个比较(按照ascii码),所以会先拿 '1' 和 '9' 比较,结果为 0.

上面的结果为 1 ,表示 MySQL 在遇到字符串和数字比较的时候,会自动把字符串转换为数字,然后再进行比较。

前面的例子一中,会走全表扫描:

//例子一的查询语句
select * from t_user where phone = 1300000001;

这是因为 phone 字段为字符串,所以 MySQL 会自动把字符串转换为数字,所以这条语句相当于:

select * from t_user where CAST(phone AS signed int) = 1300000001;

可以看到:CAST 函数作用在 phone 字段,而 phone 字段是索引,也就是对索引使用了函数,而对索引使用函数会导致索引失效

例子 2 中,会走索引扫描:

//例子二的查询语句
select * from t_user where id = "1";

这是因为字符串部分是输入参数,而id索引为整型,也就是需要将字符串转换为数字,所以这条语句相当于:

select * from t_user where id = CAST("1" AS signed int);

可以看到,索引字段并没有用任何函数,CAST 函数是用在了输入参数,因此是可以走索引扫描的。


联合索引非最左匹配

对主键字段建立的索引叫做聚簇索引,对普通字段建立的索引叫做二级索引。

那么多个普通字段组合在一起创建的索引就叫做联合索引,也叫组合索引。

创建联合索引的时候,我们需要注意创建时的顺序问题,因为 联合索引 (a,b,c) 和 (c,b,a) 在使用的时候会存在差别。

联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配。

比如,如果创建一个(a,b,c)联合索引,如果查询条件是以下几种,就可以匹配上联合索引:

  • where a = 1
  • where a = 1 and b = 2 and  c = 3
  • where a = 1 and b = 2

需要注意的是,因为有查询优化器,所以 a 字段在 where 子句的顺序并不重要。

但是,如果查询条件是以下几种,因为不符合最左匹配原则,所以就无法匹配上联合索引,联合索引就会失效:

  • where b = 2
  • where c = 3
  • where b = 2 and c = 3

有一个比较特殊的查询条件:where a = 1 and c = 3:

这种严格意义上属于索引截断,不同版本有不同的处理方式

MySQL5.5,前面的 a 会走索引,在联合索引找到主键值后,开始回表,到主键索引读取数据行,Server 层从存储引擎获取到数据行后,然后在 Server 层再比对 c 字段的值。

MySQL5.6之后,有一个索引下推功能,可以在存储引擎层进行索引遍历的过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,再返还给Server 层,从而减少回表次数。

索引下推:截断的字段不会在 Server 层进行条件判断,而是会被下推到 [存储引擎层] 进行条件判断(因为 C 字段的值是在 (a,b,c) 联合索引里的),然后过滤出符合条件的数据后再返回给 Server 层,由于在引擎层就过滤掉大量的数据,无需再回表读取数据来进行判断,减少了回表次数,从而提升了性能。

 为什么联合索引不遵循最左匹配原则就会失效?

原因是,在联合索引下,数据是按照索引第一列排序,第一列数据相同时才会按照第二列排序。

也就是说,如果想使用联合索引中尽可能多的列,查询条件中的各个列必须是联合索引中从最左边开始连续的列。如果我们仅仅按照第二列搜索,肯定无法走索引。


WHERE 子句中的 OR

在 where 子句中,如果 or 前的列是索引列,而在 or 后的条件列不是索引列,那么索引会失效

举个例子,比如下面的查询语句,id是主键, age 是普通列。从执行计划的结果看,是走全表扫描。

select * from t_user where id = 1 or age = 18;

这是因为 OR 的含义就是两个只要满足一个即可,因此只有一个条件列是索引列是没有意义的,只要有条件列不是索引列,就会进行全表扫描

解决方法:将 age 字段设置为索引即可。

可以看到 type = index merge ,index merge 的意思就是对 id 和 age 分别进行扫描,然后将这两个结果集进行合并,这样做的好处是避免了全表扫描。


总结

  • 当使用 左 或者 左右 模糊匹配的时候,也就是 like %xx 或 like %xx% 这两种方式都会造成索引失效;
  • 当我们在查询条件中对索引列使用函数,就会导致索引失效。
  • 当我们在查询条件中对索引列进行表达式计算,也无法走索引
  • MySQL在遇到字符串和数字比较的时候,会将字符串转换为数字,然后进行比较。如果字符串是索引列,而条件查询时数字,那么索引列会发生优势类型转换,由于隐式类型转换是通过 CAST 函数实现的,相当于对所有列使用了函数,所以就会导致索引失效。
  • 联合索引正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效
  • 在 WHERE 子句中,如果 OR 前的条件列是索引列,而在OR后的条件列不是索引列,那么索引会失效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/130155.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 变量的定义和数据类型的转换

变量 变量的定义 基本语法:变量名 值 变量名是给对象贴一个用于访问的标签,给对象绑定名字的过程也称为赋值,赋值符号 “” 变量名自定义,要满足标识符命名规则。 Python中,不需要事先声明变量名及其类型&#xff…

localStorage是什么?有哪些特点?

localStorage的主要作用是本地存储,它可以将数据按照键值对的方式保存在浏览器中,直到用户或者脚本主动清除数据,否则该数据会一直存在。也就是说,使用了本地存储的数据将被持久化保存。 localStorage与sessionStorage的区别是存…

春秋云镜 CVE-2015-9331

春秋云镜 CVE-2015-9331 wordpress插件 WordPress WP All Import plugin v3.2.3 任意文件上传 靶标介绍 wordpress插件 WordPress WP All Import plugin v3.2.3 存在任意文件上传,可以上传shell。 启动场景 漏洞利用 exp #/usr/local/bin/python3 # -*-coding:…

IDEA中debug调试模拟时显示不全(不显示null)的解决

IDEA中debug调试模拟时显示不全(不显示null)的解决 1、在IDEA中找到File(文件)->Settings(设置) 2、依次找到以下内容进行设置(原版、汉化版): 打开Build, Executio…

taro vue3 ts nut-ui 项目

# 使用 npm 安装 CLI $ npm install -g tarojs/cli 查看 Taro 全部版本信息​ 可以使用 npm info 查看 Taro 版本信息,在这里你可以看到当前最新版本 npm info tarojs/cli 项目初始化​ 使用命令创建模板项目: taro init 项目名 taro init myApp …

使用Puppeteer进行游戏数据可视化

导语 Puppeteer是一个基于Node.js的库,可以用来控制Chrome或Chromium浏览器,实现网页操作、截图、测试、爬虫等功能。本文将介绍如何使用Puppeteer进行游戏数据的爬取和可视化,以《英雄联盟》为例。 概述 《英雄联盟》是一款由Riot Games开…

MySQL之视图

概念 视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。视图的数据变 化会影响到基表,基表的数据变化也会影响到视图。 基本使用 创建视图 create view 视图名 as select语句; 示例&am…

纯小白安卓刷机1

文章目录 常见的英文意思刷机是什么?为什么要刷机?什么是BL锁(BootLoader锁)?我的机能够刷机吗?什么是Boot镜像/分区?什么是Recovery镜像/分区(缩写为rec)?什…

iisfastchi漏洞复现

1.查看版本 2.在路径中写入 php脚本 发现使用不了 3.环境搭建 结局打开 把限制打开

C#,数值计算——指数微分(exponential deviates)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 指数偏差 /// Structure for exponential deviates. /// </summary> public class Expondev : Ran { private double beta { get; set; } /// <s…

说完 Java 的 Abstract 后再来说说接口 (interface )

如你对 Abstract 修饰的抽象类不是非常了解的话&#xff0c;请自行先考古下。 这篇文章需要对 Java 定义过的抽象类有一些基本的了解才可以。 抽象类和抽象方法 用 Abstract 修饰的类&#xff0c;叫做抽象类&#xff0c;那么用 Abstract 修饰的方法叫做抽象方法。 在 Java 中…

Python从零到一构建项目

随着互联网的发展&#xff0c;网络上的信息量急剧增长&#xff0c;而获取、整理和分析这些信息对于很多人来说是一项艰巨的任务。而Python作为一种功能强大的编程语言&#xff0c;它的爬虫能力使得我们能够自动化地从网页中获取数据&#xff0c;大大提高了效率。本文将分享如何…

SpringMvc--CRUD

目录 一.什么是SpringMvc--CRUD 二.前期准备 公共页面跳转(专门用来处理页面跳转) 三.ssm之CRUD后端实现 配置pom.xml 双击mybatis-generator:generate自动生成mapper 编写generatorConfig.xml 项目结构 编写PagerAspect切面类 编写hpjyBiz接口类 编写hpjyBizImpl接…

《DevOps实践指南》- 读书笔记(二)

DevOps实践指南 Part 2 从何处开始5. 选择合适的价值流作为切入点5.1 绿地项目与棕地项目5.2 兼顾记录型系统和交互型系统5.3 从最乐于创新的团队开始5.4 扩大 DevOps 的范围5.5 小结 6. 理解、可视化和运用价值流6.1 确定创造客户价值所需的团队6.2 针对团队工作绘制价值流图6…

一起学数据结构(6)——栈和队列

上篇文章中&#xff0c;对栈的概念及特点进行了解释&#xff0c;并且给出了栈实现的具体代码。本篇文章将给出队列的基本概念及特点。并给出相应的代码。 1. 队列的概念及结构&#xff1a; 在给出队列的概念之前&#xff0c;先给出上篇文章中提到的栈的概念&#xff1a;一种只…

SMB 协议详解之-NTLM身份认证

前面的文章说明了SMB协议交互的过程,在SMB交互的Session Setup Request/Response会对请求者的身份进行验证,这其中涉及到两个主要的协议NTLM以及Kerberos,本文将对NTLM协议进行详细的说明。 什么是NTLM NTLM是 NT LAN Manager (NTLM) Authentication Protocol 的缩写,主要…

duffing方程matlab绘制

duffing混沌振子形式如下&#xff1a; k,a,c,f为自定义系数&#xff0c;将初值设为,k0.5,ac1 此时可通过更改f的值从0到1来改变duffing混沌系统状态&#xff0c;从固定点状态&#xff0c;小周期状态&#xff0c;混沌状态到大周期状态。例如f0.6时处于混沌状态&#xff0c;如下…

夯实网络安全基石,筑牢网络安全防线

没有网络安全就没有国家安全&#xff0c;这句话我们常常能在各种新闻里看见。安全是发展的前提&#xff0c;发展是安全的保障&#xff0c;共同推进安全和发展。Z强调&#xff1a;“要坚持依法治网、依法办网、依法上网。”今年的国家网络安全宣传周在9月11日至17日全国范围内开…

《DevOps实践指南》- 读书笔记(四)

DevOps实践指南 Part 3 第一步 &#xff1a;流动的技术实践11. 应用和实践持续集成11.1 小批量开发与大批量合并11.2 应用基于主干的开发实践11.3 小结 12. 自动化和低风险发布12.1 自动化部署流程12.1.1 应用自动化的自助式部署12.1.2 在部署流水线中集成代码部署 12.2 将部署…

【最新!七麦下载量analysis参数】逆向分析与Python实现加密算法

文章目录 1. 写在前面2. 请求分析3. 加密分析4. 算法实现1. 写在前面 之前出过一个关于榜单analysis的分析,有兴趣的可以查看这篇文章:七麦榜单analysis加密分析 最近运营团队那边有同事找到我们,说工作中偶尔需要统计分析一下某APP在一些主流应用市场的下载量趋势数据 这…