MySQL调优之索引在什么情况下会失效?

news2024/12/29 9:34:01

MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且加快查询的速度,因此索引对查询的速度有着至关重要的影响。

  • 使用索引可以快速地定位表中的某条记录,从而提高数据库查询的速度,提高数据库的性能。
  • 如果查询时没有使用索引,查询语句就会扫描表中的所有记录。在数据量大的情况下,这样查询的速度回很慢。

大多数情况下都(默认)采用B+树来构建索引。只是空间列类型的索引使用R-树,并且MEMORY表还支持hash索引。

其实,用不用索引,最终都是优化器说了算。优化器是基于什么的考虑?基于cost开销(CostBaseOptimizer),它不是基于规则(Rule-BasedOptimizer),也不是基于语义,只是依据数值大小。另外,SQL语句是否使用索引,跟数据库版本、数据量、数据选择度都有关系。

本文我们尝试总结索引失效的一些场景。我们会准备class和student两个表,class插入一万条数据,student插入50万条数据。环境是MySQL8.0,InnoDB。

【1】全值匹配我最爱

系统中经常出现的SQL语句如下:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';

建立索引前执行:

[SQL]SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
受影响的行: 0
时间: 0.308s

建立索引(age):

CREATE INDEX idx_age ON student(age);

建立索引后执行:

[SQL]SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
受影响的行: 0
时间: 0.113s

在这里插入图片描述


继续创建索引(age,classId):

CREATE INDEX idx_age_classid ON student(age,classId);

建立索引后执行:

[SQL]SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
受影响的行: 0
时间: 0.007s

在这里插入图片描述


继续创建索引(age,classId,NAME):

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);

建立索引后执行:

[SQL]SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4 AND NAME = 'abcd';
受影响的行: 0
时间: 0.000s  # 其实必然不是0,只是更小了

在这里插入图片描述

从执行计划可以看到,MySQL会帮我们选择最多包含查询列的联合索引。

【2】最佳左前缀法则

在MySQL建立联合索引时会遵守最佳左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

举例:age、name可以用到索引。

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age=30 AND student.name = 'abcd' ;

在这里插入图片描述
虽然可以正常使用,但是只有部分被使用到了。而且MySQL优化器考虑的索引是idx_age,而非idx_age_classid_name。


举例2:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid=1 AND student.name = 'abcd';

在这里插入图片描述
可以看到,没有age开头 ,完全没有用到索引。


举例3:索引idx_age_classid_name还能否正常使用?

# MySQL会进行优化,形成age,classid,name以符合联合索引idx_age_classid_name
EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE classid=4 AND student.age=30 AND student.name = 'abcd'; 

在这里插入图片描述

如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。

我们删掉索引idx_age 、idx_age_classid 再次执行查询age and name,没有中间的classid。

DROP INDEX idx_age ON student;
DROP INDEX idx_age_classid ON student;

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.name = 'abcd'; 

在这里插入图片描述
这里key_len=5,说明只用到了联合索引的一部分–age用到了索引。因为其中间环节 classid不存在, 故而不能完全使用联合索引。

结论 : MySQL可以为多个字段创建索引,一个索引可以包括16个字段。对于多列索引,过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。如果查询条件中没有使用这些字段中第一个字段时,多列(或联合)索引不会被使用。

对于=值查询,如果where中条件查询没有按照联合索引字段顺序编写,MySQL优化器会进行调优以使其满足联合索引字段顺序。

【3】主键插入顺序

对于一个使用InnoDB存储引擎的表来说,在我们没有显示的创建索引时,表中的数据实际上都是存储在聚簇索引的叶子节点的。而记录又是存储在数据页中的,数据页和记录又是按照记录主键值从小到大的顺序进行排序。所以如果我们插入的记录的主键值是依次增大的话,那我们每插满一个数据页就换到下一个数据页继续插。

而如果我们插入的主键值忽大忽小的话,就比较麻烦了。假设某个数据页存储的记录已经满了,它存储的主键值在1~100之间:
在这里插入图片描述
如果此时再插入一条主键值为9的记录,那它插入的位置就如下图:
在这里插入图片描述
可这个数据页已经满了,再插进来咋办呢?我们需要把当前页面分裂成两个页面,把本页中的一些记录移动到新创建的这个页中。页面分裂和记录移位意味着什么?意味着性能损耗! 所以如果我们想进来避免这样无谓的性能损耗,最好让插入的记录的主键值依次递增,这样就不会发生这样的性能损耗了。

所以我们建议:让主键具有AUTO_INCREMENT,让存储引擎自己为表生成主键,而不是我们手动插入,比如person_info表:

create table person_info(
	id int unsigned not null auto_increment,
	name varchar(100) not null,
	birthday date not null,
	phone_numnber char(11) not null,
	country varchar(100) not null,
	primary key (id),
	key idx_name_bd_ph_num(name(10),birthday,phone_number)
)

我们自定义的主键列id拥有AUTO_INCREMENT属性,在插入记录时存储引擎会自动为我们填入自增的主键值。这样的主键占用空间小,顺序写入,减少页分裂。

【4】计算、函数、类型转换(自动或手动)导致索引失效

如下两条SQL,哪个更好呢?其实是第一条,能够使用到索引,第二条有了函数计算。

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc'; 

我们创建索引(NAME):

CREATE INDEX idx_name ON student(NAME);

查看第一条SQL的执行计划:
在这里插入图片描述
查看第二条SQL的执行计划:
在这里插入图片描述
对比执行计划可以看到,第一条SQL使用到了索引,第二条SQL的type=all表示全表扫描。说明函数计算或导致索引失效


我们再看一下数学计算:

CREATE INDEX idx_sno ON student(stuno);

EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001;

在这里插入图片描述

如上图所示,SQL中有数学计算,执行计划中 type=all表示没有使用索引进行了全表扫描。我们再看下面这个SQL,很显然其会使用到索引。这就说明数学计算会导致索引失效

EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno = 900000;

在这里插入图片描述


最后我们再看一下类型转换

字符串类型一定不要忘记单引号,否则索引失效。

# 会进行隐式类型转换 ,索引失效
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME = 123; 

在这里插入代码片

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME = '123';

在这里插入图片描述
对比二者的执行计划可知,类型转换会导致索引失效

【5】范围条件右边的列索引失效

首先删除表student的索引:

alter table student drop index idx_name;
alter table student drop index idx_age;
alter table student drop index idx_age_classid;

查看当前索引:show index from student;
在这里插入图片描述
对于如下SQL,索引idx_age_classid_name还能够正常使用吗?

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.classId > 20 AND student.name = 'abc' ; 

执行计划如下所示,key_len=10,说明只有age和classid用到了索引。
在这里插入图片描述
这时候即使交换次序,也是没有意义的,如下所示:

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.name = 'abc' AND student.classId>20; 

那么如何使其能够使用到索引呢?如下所示创建索引(age,NAME,classId)。

CREATE INDEX idx_age_name_cid ON student(age,NAME,classId);

这时再执行上面SQL,可以看到充分用到了联合索引。
在这里插入图片描述
对于 下面这个SQL,执行计划是一样的。查询优化器对于and条件会进行顺序的调整,以满足联合索引的顺序。

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.classId > 20 AND student.name = 'abc' ; 

在这里插入图片描述

总结

  • 范围右边的列不能使用索引。比如 < 、<=、 >、 >=、 between。
  • 这个右边指的是联合索引字段的右边,至于SQL where中的and条件,查询优化器是可以进行调整的。
  • 创建的联合索引中,务必把范围涉及到的字段写在最后。

【6】不等于(!=或者 <>) 索引失效

为name字段创建索引:

CREATE INDEX idx_name ON student(NAME);

进行等值判断,正常使用索引:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name = 'abc' ;

在这里插入图片描述

对于不等判断,查看索引是否失效:

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc' ;

在这里插入代码片

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name != 'abc' ;

在这里插入图片描述
可以看到,两条SQL均为使用到索引。

【7】is null可以使用索引,is not null无法使用索引

is null可以触发索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL; 

在这里插入图片描述

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME IS NULL; 

在这里插入图片描述


is not null无法使用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL; 

在这里插入图片描述

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME IS NOT NULL; 

在这里插入图片描述

结论: 最好在设计数据表的时候就将字段设置为not null约束,比如你可以将int类型的字段,默认值设置为0.将字符类型的默认值设置为空字符('') 。同理,在查询中使用 not like 也无法使用索引,导致全表扫描。

【8】like以通配符%开头索引失效

在使用like关键字进行查询的查询语句中,如果匹配字符串的第一个字符为“%”,索引就不会起作用。只有"%"不在第一个位置,索引才会起作用。

使用到索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE 'ab%'; 

在这里插入图片描述
没有用到索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE '%ab%';

在这里插入图片描述

【9】OR前后存在非索引的列,索引失效

在where子句中,如果在or前的条件列进行了索引,而在or后的条件列没有进行索引,那么索引会失效。也就是说,OR 前后的两个条件中的列都是索引列时,查询中才会使用到索引。

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

SHOW INDEX FROM student;

# 删除索引
alter table student drop index idx_age_classid_name;
alter table student drop index idx_age_name_cid;
alter table student drop index idx_sno;
alter table student drop index idx_name;

#创建索引
CREATE INDEX idx_age ON student(age);

这时我们查询语句使用OR关键字的情况(age有索引,classid没有索引)

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;

在这里插入图片描述

可以看到,是没有使用到索引的。如果我们为classid创建索引呢?

CREATE INDEX idx_cid ON student(classid);
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;

在这里插入图片描述
可以看到,其使用到了索引,type=index_merge。简单来说,index_merge就是对age和classid分别进行了扫描,然后将这两个结果集进行了合并。这样做的好处就是避免了全表扫描。

【10】数据库和表的字符集统一使用utf8mb4

统一使用utf8mb4(5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。

不同的字符集进行比较前需要进行转换会造成索引失效。

一般性建议:

  • 对于单列索引,尽量选择针对当前query过滤性更好的索引;
  • 在选择组合索引的时候,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好;
  • 在选择组合索引的时候,尽量选择能够包含当前query中的where子句中更多字段的索引;
  • 在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面。

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

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

相关文章

Spring JdbcTemplate.queryForObject()

Spring JdbcTemplate 是JDBC核心包中的中心类。它简化了 JDBC 与 Spring 的使用&#xff0c;并有助于避免常见错误。在此页面上&#xff0c;我们将学习使用它的queryForObject 方法。 JdbcTemplate.queryForObject不同参数的方法。1. <T> T queryForObject(String sql, …

继承-安全-设计模式

继承 与 原型、原型链 1. 继承是什么&#xff1f; 继承就是一个对象可以访问另外一个对象中的属性和方法 2. 继承的目的&#xff1f; 继承的目的就是实现原来设计与代码的重用 3. 继承的方式 java、c等&#xff1a;class**javaScript&#xff1a; 原型链 ** ES2015/ES6 中…

数据导入与预处理-拓展-pandas可视化

数据导入与预处理-拓展-pandas可视化1. 折线图1.1 导入数据1.2 绘制单列折线图1.3 绘制多列折线图1.4 绘制折线图-双y轴2. 条形图2.1 单行垂直/水平条形图2.2 多行条形图3. 直方图3.1 生成数据3.2 透明度/刻度/堆叠直方图3.3 拆分子图4. 散点图4.1生成数据4.2 绘制大小不一的散…

自动化测试的使用场景有哪些?如何正确使用?

目录 前言 什么是自动化测试&#xff1f; 自动化测试的使用场景有哪些&#xff1f; 自动化测试有什么好处&#xff1f; 总结 前言 本文将通过介绍 自动化测试是什么 &#xff0c; 哪些场景适用于自动化测试 &#xff0c; 自动化测试的好处 &#xff0c; 以及通过 具体的自…

vue如何二次封装一个高频可复用的组件

在我们的业务里&#xff0c;我们通常会二次封装一些高频业务组件&#xff0c;比如弹框&#xff0c;抽屉&#xff0c;表单等这些业务组件&#xff0c;为什么要二次封装&#xff1f;我们所有人心里的答案肯定是&#xff0c;同样类似的代码太多了&#xff0c;我想复用组件&#xf…

2004-2020中小企业板上市公司财务报表股票交易董事高管等面板数据

1200变量&#xff01;中小企业板上市公司面板数据大全 2004-2020年 1、时间&#xff1a;2004-2020年 2、数据范围&#xff1a;共计973家上市公司 3、数据指标&#xff1a;包括财务报表、股票交易、董事高管等1200变量 4、用途&#xff1a;进行上市公司高管股权激励与公司绩…

C语言刷题系列——1.将三个整数按从大到小输出

将三个整数按从大到小输出1.输入三个整数2.最大的值放在a中&#xff0c;最小值放在c中&#xff0c;剩余的一个放在bstep1&#xff1a;a和b比较step2&#xff1a;a和c比较step3&#xff1a;b和c比较3.最终的代码1.输入三个整数 先写好main函数、头文件 #include <stdio.h&g…

用高并发技巧解决redis热key问题

​ 这篇文章我将介绍工作中处理热key问题的常用手段&#xff0c;可能介绍的不是很全&#xff0c;毕竟不同的业务场景可能有不同的解决方案&#xff0c;但是相信通过这部分的介绍能提供一个热key问题的思路。 热key问题&#xff0c;简单来说就是对某一资源的访问量过高问题&…

Unity学习shader笔记[一百零八]简单萤火效果

之前用粒子系统基于原有萤火虫的粒子改了一波慢萤火效果就被惊艳到了&#xff0c;开始大家讨论&#xff0c;就都觉得这样大数量的粒子消耗挺大的&#xff0c;后面测试过才发现单纯的粒子系统在总粒子数量3000&#xff0c;每秒300的生成数量&#xff0c;屏幕呈现有1000多个粒子的…

【黄啊码】MySQL入门—17、在没有备份的情况下,如何恢复数据库数据?

大家好&#xff01;我是黄啊码&#xff0c;MySQL的入门篇已经讲到第16个课程了&#xff0c;今天我们继续讲讲大白篇系列——科技与狠活之恢复数据库 在没做数据库备份&#xff0c;没有开启使用 Binlog 的情况下&#xff0c;尽可能地找回数据。 今天的内容主要包括以下几个部分…

2022NISCTF--web

easyssrf 打开题目&#xff0c;显示的是 尝试输入&#xff0c; 发现输入flag有东西 读取文件 访问下一个网站 读取文件 不能以file开头 直接伪协议&#xff0c;base64解码 checkIn 奇怪的unicode编码 当选中左边的时候右边也会被选中 我们在vscode看看 这样的额 展示的是UTF-1…

Linux系统中利用open函数多次打开同一个文件操作方法

大家好。 今天的话主要和大家聊一聊&#xff0c;在Linux系统中如果一个文件被打开多次会出现什么情况。 目录 第一&#xff1a;多次打开同一个文件 ​第二&#xff1a;一个文件被打开多次&#xff0c;在内存中不会存在多份动态文件 ​第三&#xff1a;多次open打开同一…

第一章 - Windows安装VMware Workstation Pro

文章目录前言一、VMware Workstation Pro安装的前提条件二、VMware Workstation Pro下载三、VMware Workstation Pro安装前言 Linux是一个开源、免费的操作系统&#xff0c;其稳定性、安全性、处理多并发已经得到业界认可&#xff0c;目前很多企业级的项目都会部署到Linux系统…

结构体内存对齐

在知道了结构体类型的基本使用之后&#xff0c;我们需要深入探讨一个问题&#xff0c;即计算结构体的大小&#xff0c;这也是一个热门的考点&#xff1a;结构体内存对齐。 目录 一、结构体的对齐规则 二、例题 2.1 例题一 2.2 例题二 2.3 例题三 ​编辑 三、为什么存在内存…

【C++】vector,list迭代器失效

1.vector迭代器失效 vector容器的物理基础是线性表&#xff0c;底层是指针变量实现的。 在这里导致vector迭代器失效的原因会有两种-----插入失效&#xff0c;删除失效。 1.2插入数值导致迭代器失效 1.21扩容导致迭代器失效 我们在一块vector空间插入pos&#xff08;20&…

第三章 单向链表的讲解与实现

初阶数据结构 第一章 时间复杂度和空间复杂度 第二章 动态顺序表的实现 第三章 单向链表的讲解与实现 文章目录初阶数据结构前言一、什么是链表&#xff1f;二、节点的定义&#xff1a;三、单向链表接口函数1、打印&#xff1a;2、尾插&#xff1a;3、头插&#xff1a;4、尾删…

改进YOLOv7系列: 最新结合用于小目标的新CNN卷积构建块

&#x1f4a1;统一使用 YOLOv7 代码框架&#xff0c;结合不同模块来构建不同的YOLO目标检测模型。&#x1f31f;本项目包含大量的改进方式,降低改进难度,改进点包含【Backbone特征主干】、【Neck特征融合】、【Head检测头】、【注意力机制】、【IoU损失函数】、【NMS】、【Loss…

Linux-进程控制

进程控制进程创建fork函数写时拷贝fork常规用法fork调用失败的原因进程终止进程等待进程程序替换程序替换的原理如何程序替换进程创建 fork函数 fork之前父进程独立运行&#xff0c;fork之后&#xff0c;父子两个执行流分别执行。 进程具有独立性&#xff0c;代码和数据必须独立…

机器学习HMM模型

目录1 马尔科夫链1.1 简介1.2 经典举例1.3 小结2 HMM简介2.1 简单案例2.2 案例进阶2.2.1 问题阐述2.2.2 问题解决3 HMM模型基础3.1 什么样的问题需要HMM模型3.2 HMM模型的定义3.3 一个HMM模型实例3.4 HMM观测序列的生成3.5 HMM模型的三个基本问题4 前向后向算法评估观察序列概率…

计算机毕业设计-SSM高校社团招新系统-JavaWeb大学生社团管理系统-源码+文档+讲解

注意&#xff1a;该项目只展示部分功能&#xff0c;如需了解&#xff0c;评论区咨询即可。 本文目录1.开发环境2.系统的设计背景3 前后台功能设计3.1 前台功能3.2 后台功能4 系统页面展示4.1 学生功能模块展示4.2 干部功能模块展示4.3 管理员功能模块展示5 更多推荐6 部分功能代…