索引失效有哪些?

news2024/12/23 22:42:23

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

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

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


索引存储结构长什么样?

索引的存储结构跟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.coloradmin.cn/o/1000212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美业创新之路:广告电商模式让你的品牌脱颖而出

美业是一个巨大的市场,但也面临着激烈的竞争和消费者的多样化需求。如何在这个市场中脱颖而出,实现品牌的增长和盈利呢?答案就是广告电商模式。 广告电商模式是一种结合了社交电商和广告分佣的新型电商模式,它可以让消费者在购物的…

几种研发管理流程

一、CMMI 1.初始阶段 软件过程混乱,有时甚至混乱。几乎没有流程的定义。成功取决于个人的努力。管理是被动的。 2.可重复/可管理 建立了基本的项目管理流程来跟踪成本,进度和功能特征。已经建立了必要的过程规程,以便能够重复先前类似应用…

RPC框架核心技术

一、RPC框架整体架构 RPC Client && RPC Server RPC Client 1、动态代理,根据lookUp信息(接口-实现-方法)动态创建出代理类,(创建代理类RPC服务端的目标接口)。即Lookup为远端目标接口地址&#…

localStorage是什么?有哪些特点?

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

Cpolar+Tipas:在Ubuntu上搭建私人问答网站,为您提供专业的问题解答

文章目录 前言2.Tipask网站搭建2.1 Tipask网站下载和安装2.2 Tipask网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道(云端设置)3.3 Cpolar稳定隧道(本地设置) 4. 公网访问测试5. 结语 前…

什么是JavaScript中的严格模式(strict mode)?应用场景是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 严格模式(Strict Mode):⭐ 使用场景⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&…

shell脚本指令:for循环、函数、数组、grep等指令的使用

1、实现一个对数组求和的函数,数组通过实参传递给函数 2、写一个函数,输出当前用户的uid和gid 并使用变量接收结果 #!/bin/bash echo "请输入一个数组" read -a arr function add_arr() {var1${#arr[*]}for i in ${arr[*]} do((sumi))doner…

技术解码 | GB28181/SIP/SDP 协议--EasyGBS国标GB28181平台国标视频技术SIP解析

EasyGBS国标视频云服务是基于国标GB/T28181协议的视频能力平台,可实现的视频功能包括:实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。平台部署简单、可拓展性强,支持将接入的视频流进行全终端、全平台分发&#…

中国人民大学与加拿大女王大学金融硕士——人生总要逼自己一把

我们每个人都是一个独特而丰富的个体,身上蕴藏着各种潜力和可能性。要不断去开发自己的潜能,不断学习和提升自己的知识和技能,保持对新知识和趋势的敏感。想要在职场上走得更远,就要逼自己一把,在职继续攻读硕士学位是…

82 # koa-bodyparser 中间件的使用以及实现

准备工作 安装依赖 npm init -y npm i koakoa 文档:https://koajs.cn/# koa 中不能用回调的方式来实现,因为 async 函数执行的时候不会等待回调完成 app.use(async (ctx, next) > {console.log(ctx.path, ctx.method);if (ctx.path "/login…

518抽奖软件,是否会重复中奖,还是没人只能抽中一次

518抽奖软件简介 518抽奖软件,518我要发,超好用的年会抽奖软件,简约设计风格。 包含文字号码抽奖、照片抽奖两种模式,支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。(www.518cj.net) 不会重复中奖 类似抽奖箱的概念&#xff0c…

【1++的数据结构】之哈希(二)

👍作者主页:进击的1 🤩 专栏链接:【1的数据结构】 文章目录 一,前言二,位图1. 位图2. 位图的应用 三,布隆过滤器 一,前言 上一节我们讲解了哈希表,简单的了解了哈希思想…

探索工业4.0:数字孪生如何重塑工业生产流程?

在过去的几十年里,工业生产经历了从机械化、自动化到数字化的巨大转变。随着工业4.0的到来,我们正处于第四次工业革命的边缘,这次革命将由数字孪生技术引领。本文将深入探讨数字孪生在工业生产中的应用和潜力。 数字孪生(Digital …

第六章 进程管理与系统监控

第六章 进程管理与系统监控 ​ 一个具有较好的安全性和稳定性的系统是用户所需要的。无论进行何种操作和业务处理,用户都希望系统始终处于安全、稳定的状态。因此,即时地进行系统的进程管理和系统监控工作是保证系统安全、稳定的状态。 1.进程管理 1.…

C++项目实战——基于多设计模式下的同步异步日志系统-⑥-日志等级类与日志消息类设计

文章目录 专栏导读日志等级类设计日志等级划分to_string函数设计日志等级类整理 日志消息类设计 专栏导读 🌸作者简介:花想云 ,在读本科生一枚,C/C领域新星创作者,新星计划导师,阿里云专家博主,…

第4篇 vue的 ECMAScript 6的学习

一 ECMAScript 6 1.1 ECMAScript 6 ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。 因此,ES6 既是一个历史名词,也…

java+ssm+mysql高校图书管理系统

项目介绍: 本系统为基于jspssmmysql的高校图书管理系统,包含管理员、学生角色,功能如下: 管理员(高级管理和普通管理):用户管理(管理员和学生管理);图书管理…

ps制作gif动图

最后存储就可以了

问道管理:历史市净率在哪看?

市净率是评价一家企业股票价格是否合理的一项重要指标,并且也能够反映企业的财务状况。前史市净率则是指某家企业在曩昔一段时间内(比方一年或三年)的市净率平均值。那么,前史市净率在哪里能够查询呢?本文将从多个视点…

华为OD机试 - 一种字符串压缩表示的解压(Java 2022 Q4 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、题意2、根据题意,不合法方式如下:3、解题思路 五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为O…