索引失效的几种场景

news2024/11/20 6:35:45

索引失效的几种场景

  • 初始化数据
  • 一、对索引使用左或左右模糊匹配
  • 二、对索引使用函数
  • 三、对索引使用表达式计算
  • 四、对索引进行隐式类型转换
  • 五、索引使用不满足最左前缀原则
  • 六、where子句使用or
  • 总结

初始化数据

本文使用的是InnoDB存储引擎,先来创建一个学生表。

drop procedure if exists add_student;

CREATE PROCEDURE `add_student`(in n int, in batchNum int)
BEGIN
    DECLARE i INT DEFAULT 1;
    DECLARE age1 INT DEFAULT 1;
    DECLARE gender1 INT DEFAULT 1;
    WHILE (i < n + 1)
        DO
            set age1 = floor(18 + (rand() * 5));
            set gender1 = floor(rand() * 3);
            set autocommit = 0;
            INSERT into student (order_num, student_name, age, create_time, gender)
            VALUES (i, concat('student_name', i), age1, now(), gender1);
            set i = i + 1;
            if i mod batchNum = 0 then
                commit;
            end if;
        END WHILE;
    commit;
END

这里使用存储过程来插入100万条数据。

--创建存储过程

drop procedure if exists add_student;

CREATE  PROCEDURE `add_student`(in n int,in batchNum int) 
BEGIN
    DECLARE i INT DEFAULT 1;
		 DECLARE age1 INT DEFAULT 1;
		  DECLARE gender1 INT DEFAULT 1;
	WHILE (i < n+1 ) DO
	set age1=floor(18+( rand() * 5));
	set gender1 = floor(rand() * 3);
	set autocommit = 0;
		INSERT into student (order_num,name,age,create_time,gender) VALUES (i,concat('student_name',i),age1,now(),gender1);
		set i=i+1;
		if i mod batchNum = 0 then
                commit;
     end if;
	END WHILE;
	 commit;
END
-- 调用
CALL add_student(1000000,100000) 

在这里插入图片描述
查看表中的索引:

show index from student;

在这里插入图片描述

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

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

我们对name字段添加索引:

CREATE INDEX idx_student_name ON student(name(20));

再次查看表中的索引:

show index from student;

在这里插入图片描述
比如下面的 like 语句,查询 name 后缀为「name112」的用户,执行计划中的 type=ALL 就代表了全表扫描,而没有走索引。

# 此时name字段为二级索引
explain select * from student where name like '%name112';

在这里插入图片描述
如果是查询 name 前缀为「student_name999」用户,那么就会走索引扫描,执行计划中的 type=range 表示走索引扫描,key=index_name 看到实际走了 index_name 索引:

// 此时name字段为二级索引
explain select count(1) from student where name like 'student_name999%';

在这里插入图片描述

问:为什么 like 关键字左或者左右模糊匹配无法走索引呢?
答:因为索引 B+ 树是按照「索引值」有序排列存储的,只能根据前缀进行比较。

举个例子,下面这张二级索引图(图中叶子节点之间我画了单向链表,但是实际上是双向链表,原图我找不到了,修改不了,偷个懒我不重画了,大家脑补成双向链表就行),是以 name 字段有序排列存储的。

在这里插入图片描述
假设我们要查询 name 字段前缀为「林」的数据,也就是 name like ‘林%’,扫描索引的过程:

  1. 首节点查询比较:林这个字的拼音大小比首节点的第一个索引值中的陈字大,但是比首节点的第二个索引值中的周字小,所以选择去节点2继续查询;

  2. 节点 2 查询比较:节点2的第一个索引值中的陈字的拼音大小比林字小,所以继续看下一个索引值,发现节点2有与林字前缀匹配的索引值,于是就往叶子节点查询,即叶子节点4;

  3. 节点 4 查询比较:节点4的第一个索引值的前缀符合林字,于是就读取该行数据,接着继续往右匹配,直到匹配不到前缀为林的索引值。

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

二、对索引使用函数

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

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

explain select * from student where length(name)=16;

在这里插入图片描述

为什么对索引使用函数,就无法走索引了呢?

因为索引保存的是索引字段的原始值,而不是经过函数计算后的值,自然就没办法走索引了。

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

三、对索引使用表达式计算

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

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

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

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

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

在这里插入图片描述

为什么对索引进行表达式计算,就无法走索引了呢?

原因跟对索引使用函数差不多。

因为索引保存的是索引字段的原始值,而不是 id + 1 表达式计算后的值,所以无法走索引,只能通过把索引字段的取值都取出来,然后依次进行表达式的计算来进行条件判断,因此采用的就是全表扫描的方式。

有的同学可能会说,这种对索引进行简单的表达式计算,在代码特殊处理下,应该是可以做到索引扫描的,比方将 id + 1 = 10 变成 id = 10 - 1。

是的,是能够实现,但是 MySQL 还是偷了这个懒,没有实现。

我的想法是,可能也是因为,表达式计算的情况多种多样,每种都要考虑的话,代码可能会很臃肿,所以干脆将这种索引失效的场景告诉程序员,让程序员自己保证在查询条件中不要对索引进行表达式计算。

四、对索引进行隐式类型转换

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

仍然用 student 表中的 name 字段,是二级索引且类型是 varchar。

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

explain select * from student where name = 1;

在这里插入图片描述
然后我们再用字符串作为输入参数,此时执行计划中type = index,就变成通过索引插叙数据了。

explain select count(1) from student where name = '1';

在这里插入图片描述

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

要明白这个原因,首先我们要知道 MySQL 的数据类型转换规则是什么?就是看 MySQL 是会将字符串转成数字处理,还是将数字转换成字符串处理。

我在看《mysql45讲的时候》看到一个简单的测试方式,就是通过 select “10” > 9 的结果来知道MySQL 的数据类型转换规则是什么:

  • 如果规则是 MySQL 会将自动「字符串」转换成「数字」,就相当于 select 10 > 9,这个就是数字比较,所以结果应该是 1;
  • 如果规则是 MySQL 会将自动「数字」转换成「字符串」,就相当于 select “10” > “9”,这个是字符串比较,字符串比较大小是逐位从高位到低位逐个比较(按ascii码) ,那么"10"字符串相当于 “1”和“0”字符的组合,所以先是拿 “1” 字符和 “9” 字符比较,因为 “1” 字符比 “9” 字符小,所以结果应该是 0。

在 MySQL 中,执行的结果如下图:
在这里插入图片描述
上面的结果为 1,说明 MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。

前面的例子一中的查询语句,我也跟大家说了是会走全表扫描:

explain select * from student where name = 1;

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

select * from student where CAST(name AS signed int) = 1;

可以看到,CAST 函数是作用在了 phone 字段,而 phone 字段是索引,也就是对索引使用了函数!而前面我们也说了,对索引使用函数是会导致索引失效的。

例子二中的查询语句,我跟大家说了是会走索引扫描:

explain select count(1) from student where name = '1';

这时因为字符串部分是输入参数,也就需要将字符串转为数字,所以这条语句相当于:

select * from student where name = 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 ,符合最左匹配吗?

这种其实严格意义上来说是属于索引截断,不同版本处理方式也不一样。

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

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

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

比如下面这条 where a = 1 and c = 0 语句,我们可以从执行计划中的 Extra=Using index condition 使用了索引下推功能。
在这里插入图片描述

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

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

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

打个比方,老师让你去教学楼3楼ES304教室取文件,但是不告诉你一楼的入口,只告诉你二楼三楼怎么走,相当于是空中楼阁,根本无法找得到。

六、where子句使用or

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

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

explain select * from student where id = 1 or age = 18;

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

要解决办法很简单,将 age 字段设置为索引即可。

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

总结

今天给大家介绍了 6 种会发生索引失效的情况:

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

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

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

相关文章

2021年山东省职业院校技能大赛高职组“信息安全管理与评估”样题

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

【第二十五课】动态规划:数字三角形(acwing-898 / 蓝桥官网503 / c++代码)

目录 acwing-898数字三角形(模板题) 思路 注意点 代码 视频讲解推荐 2020蓝桥杯省赛-数字三角形 错误思路 (可不看) 思路 代码 注意点 续上之前的啦。 【第二十五课】动态规划&#xff1a;01背包问题(acwing-2 / 思路 / 含一维数组优化 / c代码) 适合在学习过背包…

OS复习笔记ch3-1

引言 学到第三章&#xff0c;就正式步入我们OS的大门了 本章我们将围绕以下几个问题去解决 什么是进程&#xff1f;进程状态有哪些&#xff1f;进程如何描述&#xff1f;进程如何控制&#xff1f; 本节内容主要是回答前两个问题&#xff0c;第二节回答后两个问题。 进程 …

windows环境下安装Apache

首先apache官网下载地址&#xff1a;http://www.apachelounge.com/download/按照自己的电脑操作系统来安装 这里我安装的是win64 主版本是2.4的apache。 然后解压压缩包到一个全英文的路径下&#xff01;&#xff01;&#xff01;一定一定不要有中文 中文符号也不要有&#xff…

详细分析PyInstaller打包python为exe执行文件(附Demo)

目录 前言1. 基本知识2. Demo 前言 需要将python文件打包成exe文件&#xff0c;变成rpa自动化形式 1. 基本知识 PyInstaller是一个用于将Python应用程序打包成独立可执行文件的工具 可以将Python脚本打包成Windows、Linux和Mac OS X上的可执行文件&#xff0c;这个作用可以将…

【Linux】软硬链接与动静态库(理解软硬链接的特点及使用场景、如何建立动静态库与使用第三方库)

一、软链接 1.1 如何建立软链接 //建立软链接 -s代表soft ln -s 目标文件名 链接文件名//删除软链接 rm 链接文件 或 unlink 链接文件 1.2 软链接的特点与功能 通过ls -i指令可以查看文件的inode编号 、 可以看出目标文件与软链接文件各自有自己的inode&#xff0c;所以软…

揭秘App全渠道统计服务:如何精准追踪你的用户来源?

在移动互联网时代&#xff0c;App的推广和运营至关重要&#xff0c;而渠道统计则是衡量推广效果的关键一环。近日&#xff0c;xinstall推出了一款全新的App全渠道统计服务&#xff0c;该服务旨在帮助开发者和运营者更全面地了解用户来源&#xff0c;优化推广策略&#xff0c;从…

前端JS加密库CryptoJS的常用方法

CryptoJS是前端常用的一个加密库&#xff0c;如MD5、SHA256、AES等加密算法。 官方文档&#xff1a;https://www.npmjs.com/package/crypto-js 安装方法 方法一&#xff1a;直接在html文件中引入 <script type"text/javascript" src"path-to/bower_componen…

C# 项目:导线计算 / 坐标转换 / 曲线放样 / 水准网 / 导线网平差

文章目录 Part.I IntroductionPart.II 软件简介Chap.I 导线计算Chap.II 坐标转换Chap.III 曲线放样Chap.IV 水准网 / 导线网平差 Part.III 软件使用过程中可能遇到的问题Reference Part.I Introduction 本文将对几个基于 C# 开发的软件进行简要的介绍&#xff0c;这些软件都是…

前端更优雅的使用 jsonp

前端更优雅的使用 jsonp 背景&#xff1a;最近项目中又使用到了 jsonp 这一项跨域的技术&#xff0c;&#xff08;主要还是受同源策略影响&#xff09;&#xff0c;下面有为大家提供封装好的函数及对应使用示例&#xff0c;欢迎大家阅读理解 文章目录 前端更优雅的使用 jsonp同…

SAP-ERP TM运输管理模块详解-1

简介 SAP中的运输功能(即TM模块,属于SD的子模块)是后勤执行的一部分,用于自动计算交货成本。也就是说,SAP可以让系统自动对销售发货的商品计算运费,对于运费占这个成本很大比重的销售模式,可以使用该功能。运输功能相对于SD其他模块,相对比较独立的,应用面不是很广。其…

【算法一则】【贪心】数组中的数可以拼装成的最大数

题目 给定一组非负整数 nums&#xff0c;重新排列每个数的顺序&#xff08;每个数不可拆分&#xff09;使之组成一个最大的整数。 注意&#xff1a;输出结果可能非常大&#xff0c;所以你需要返回一个字符串而不是整数。 示例 1&#xff1a; 输入&#xff1a;nums [10,2] …

使用 frp 通过云厂商公网IP实现内网穿透

写在前面 有小伙伴推荐&#xff0c;简单了解博文内容涉及 内网穿透 工具 frp 的安装以及2个Demo内网的静态文件服务访问 Demo内网多端口映射 Demo理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的…

【数据分析面试】32.矩阵元素求和 (Python: for…in…语句)

题目&#xff1a;矩阵元素求和 &#xff08;Python) 假设给定一个整数矩阵。你的任务是编写一个函数&#xff0c;返回矩阵中所有元素的和。 示例 1&#xff1a; 输入&#xff1a; matrix [[1, 2, 3], [4, 5, 6], [7, 8, 9]]输出&#xff1a; matrix_sum(matrix) -> 45…

判断n以内的素数个数的五种方法+时间对比

目录 方法一&#xff1a;暴力法 复杂度 方法二&#xff1a;跨度为6的倍数的优化 复杂度 方法三&#xff1a;埃氏筛法 复杂度 方法四&#xff1a;埃氏筛法的改良 复杂度 方法五&#xff1a;线性筛 复杂度 性能对比测试 练习 方法一&#xff1a;暴力法 就是写一个函…

STL--string详解

STL基本内容 string是什么 string实质上是一个对象 string可看作一个串&#xff0c;类似字符数组 可以扩容&#xff0c;可以增删查改 可用下表访问操作符[]引用&#xff0c;修改某值 构造函数 默认构造 拷贝构造&#xff1a;参数为(string 或 char*) 求string对象的长度不…

锂电池SOH预测 | 基于SVM的锂电池SOH预测(附matlab完整源码)

锂电池SOH预测 锂电池SOH预测完整代码锂电池SOH预测 锂电池的SOH(状态健康度)预测是一项重要的任务,它可以帮助确定电池的健康状况和剩余寿命,从而优化电池的使用和维护策略。 SOH预测可以通过多种方法实现,其中一些常用的方法包括: 容量衰减法:通过监测电池的容量衰减…

C语言学习/复习36

一、程序的环境与预处理 二、翻译环境与执行环境 三、运行环境 四、预编译(预处理)详解

【Hadoop】-Apache Hive使用语法与概念原理[15]

一、数据库操作 创建数据库 create database if not exists myhive; 使用数据库 use myhive; 查看数据库详细信息 desc database myhive; 数据库本质上就是在HDFS之上的文件夹。 默认数据库的存放路径是HDFS的&#xff1a;/user/hive/warehouse内 创建数据库并指定hdfs…

SVN--基本原理与使用(超详细)

目录 一、SVN概述二、SVN服务端软件安装三、SVN服务端配置四、SVN客户端软件安装与使用五、SVN三大指令六、SVN图标集与忽略功能6.1 图标集6.2 忽略功能 七、SVN版本回退八、SVN版本冲突九、SVN配置多仓库与权限控制9.1 配置多仓库9.2 权限控制 十、服务配置与管理十一、模拟真…