【MySQL】SQL索引失效的几种场景及优化

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

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

我们一般创建的索引类型都是B+Tree结构,其实,用不用索引最终都是优化器说了算。

那么MySQL优化器是什么呢?

MySQL内部优化器是MySQL中很重要的一个部分,它主要用于在执行查询时获取最合适的执行计划,以使得查询能够以最短的时间内得到结果。

MySQL内部优化器的工作原理是在接收到一条查询语句之后,它会根据一系列的算法和规oSi则来确定哪个执行计划是最优的。

通常情况下,MySQL优化器会依赖于表的统计信息和索引信息来进行优化决策。例如,在执行select语句时,优化器会尝试使用索引来避免全表扫描。同时,优化器还会对各种查询操作的代价进行估算,以便找到最优的执行计划。

在这里插入图片描述

那么我们如何去查看这条sql的一个执行计划呢?很简单只需要在我们执行的sql前面加上explain关键字即可。

-- 创建数据库表格
CREATE TABLE `account` (`id` bigint NOT NULL AUTO_INCREMENT,`phone` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,`pwd` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,`province` varchar(80) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,`city` varchar(60) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,`status` int NOT NULL,`gmt_create` datetime DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-- 执行sql查看执行计划
explain SELECT * from account where id = '1'

在这里插入图片描述

EXPLAIN 是用于分析并优化查询语句性能的工具,执行计划会解析查询语句并生成执行计划,包括访问表和索引所需的策略、查询优化器的选择以及每个阶段的估计记录数等信息,深入SQL语句在执行过程中的各个细节。

OK,接下来我们来看一下各个字段的含义。

id:对于 SELECT 语句,每个查询都会被分配一个唯一的ID。表示查询的标识符,数字越大越先执行。

select_type:表示查询类型或者子查询类型,使用不同的 select_type 来帮助评估查询性能,并确定可以采取哪些优化方法。需要根据具体情况来进行相应的优化,例如尽量减少子查询的数量,避免使用不必要的 UNION 操作等等。

类型概述
SIMPLE表示简单的 SELECT 查询,不包含子查询或 UNION 操作。
PRIMARY表示外层查询的第一个 SELECT
UNION表示 UNION 操作的第二个或后续的 SELECT 查询。
SUBQUERY表示一个子查询,MySQL 会在子查询中先执行查询,比如where里面包括了子查询
DEPENDENT SUBQUERY也表示一个子查询,但是外部 SQL 查询的结果会影响子查询的执行
DERIVED表示派生表,MySQL 会在查询中创建一个新的临时表,这个临时表来自于 FROM 子句中的子查询
UNION RESULT表示 UNION 操作的结果,MySQL 在创建结果集时使用临时表来存储数据

table:表示查询涉及到哪些表,对于子查询等复杂查询可能涉及多张表。

partitions:表示查询操作涉及到的分区表的分区情况。

type:表示 MySQL 在表中找到所需行的方式,常见的类型包括 ALL, index,range, ref, eq_ref, const, system, NULL。

Type概述
all全表扫描,MYSQL扫描全表来找到匹配的行
index索引全扫描,MYSQL遍历整个索引来查找匹配的行;Extra 字段里面 出现 Using index,则是覆盖索引,不用二次回表查询
range索引范围扫描,常见于<、<=、>、>=、between、in等操作符;相对于index的全索引扫描,它有范围限制,因此要优于index
ref使用非唯一性索引或者唯一索引的前缀扫描,返回匹配某个单独值的记录行;
虽使用了索引但该索引列的值并不唯一,进行目标值附近的小范围扫描,不扫描全表
eq_refeq_ref 与 ref对比结果集只有一个,使用主键或者唯一索引进行查找,不用扫描更多行
const最多只有一条匹配行,查询非常迅速,用到primary key 或者unique key,性能最高
system表只有一行,基本不会出现,忽略
null不访问数据库表,直接返回索引

possible_key:表示 MySQL 可以使用哪些索引来优化查询

key:表示 MySQL 实际使用的索引,如果没有使用任何索引,则该值为 NULL

key_len:表示 MySQL 实际使用的索引的长度,该值与索引定义的长度有关

ref:表示 MySQL 使用哪个列或常量与索引列进行比较。

rows:表示 MySQL 估计要扫描多少行才能找到所需记录,是一个估算值而不是确切值。

filtered:查询条件过滤的效率,百分比形式表示, Filtered 越高,表示查询结果集中过滤数据所需要的开销越小,查询性能就越好。

Extra:该字段包括一些额外的查询信息,包括使用何种排序方式、使用哪种 Join 操作等。

类型概述
Using index选择使用了覆盖索引的特性,通过索引直接获取查询结果,而无需回表查询,提高了查询效率。
Using filesort需要额外进行 一个文件排序操作来实现 ORDER BY 操作,可能会严重影响查询性能。
Using temporary在执行查询时需要借助临时表来保存中间结果集,这常发生在排序、分组、子查询和 UNION 查询之中。
Using where条件查询,在查询过程中需要进行表级别的条件过滤,即使共享了某些索引,也需要进行全表扫描查找符合条件的行。不是仅仅通过索引就可以获取所有需要的数据,则会出现 Using where
Range checked for each record通过索引比较操作来过滤部分行,直到找到符合条件的行,这种操作常出现在使用 INDEX 和 ORDER BY 操作时。
Using join buffer (Block Nested Loop)在执行连接操作时需要额外申请 join buffer 来存储中间结果,这种操作常发生在连接操作中。
Using index condition利用了查找索引数据的过程中额外发现的过滤条件进行了优化,无需回表查询或查表,可以直接通过索引结果来返回查询的结果
Using sort_union()Using union()通过 UNION ALL 或 UNION DISTINCT 操作来合并查询结果集,使用了一些优化策略来提高查询效率。

OK,介绍了这么多,下面我们就开始进入正题,来说一说索引失效的场景都有哪些。

1.隐式转换导致不走索引,索引失效

当采用索引查询时列的类型不一样,就会导致索引失效。我们当前account表中id是varchar类型,我们现在查询用数字类型查,这会就会导致索引失效。

explain SELECT * from account where id = 1

在这里插入图片描述

改用字符串查询

explain SELECT * from account where id = '1'

在这里插入图片描述

2.当索引列配合不是索引列进行or查询时,索引失效

当我们查询时索引列配合不是索引列进行查询的时候,会导致索引失效,比如说id是索引,gmt_create不是索引,当**id = ‘1’ or gmt_create = ‘2024-01-01’**时,这就会导致索引失效。

explain SELECT * from account where id='1' or  gmt_create = '2024-01-01'

在这里插入图片描述

要想让其走索引查询,可以给gmt_create加上索引,or两边字段都是索引字段才会走索引

CREATE INDEX gmt_create on account(gmt_create)

在这里插入图片描述

这里也可以我们规定强制走哪一个索引,不过一般不建议,因为sql优化器已经帮我们计算好最优的查询方式。

explain SELECT * from account force index(PRIMARY) where id='1' or  gmt_create = '2024-01-01'

在这里插入图片描述

虽然强制地使用了索引,但是经过分析,这次查询还是没有使用索引,所以强制使用索引并不一定是生效的。

3.业务表的数据量太少,索引失效

MySQL索引是为了加速查询而存在的,如果数据量太小,MySQL查询速度本来就很快,这时候使用索引反而会拖慢查询速度。因此,当数据量很小的时候,MySQL索引可能会失效。

explain SELECT * from account where id = '1'

在这里插入图片描述

4.当索引字段采用函数查询时,索引失效

当索引字段采用函数查询时,会导致索引失效,比如gmt_create本身是一个索引字段,我们采用YEAR函数进行查询,就会导致索引的失效。

explain SELECT * from account where YEAR(gmt_create)  =  '2023'

在这里插入图片描述

5.like查询索引字段左边模糊查询,索引失效

当索引列使用LIKE操作符时,左边模糊查询会导致索引失效。比如我们给province加上索引,我们用province like “%天津%” 或者 province like “%天津” 都会导致索引的失效,只有province like "天津%"索引才不会失效。

添加索引:CREATE INDEX province on account(province)

explain SELECT * from account where province like '%天津'

在这里插入图片描述

explain SELECT * from account where province like '天津%'

在这里插入图片描述

6.字段重复性高导致索引失效

比如有一些字段他的重复性的值确实特别的高,那么这种字段就不适合加索引。

在这里插入图片描述

explain SELECT * from account where province = '宝地区'

在这里插入图片描述

7.IS NULL操作时,索引失效

IS NULL不走索引,IS NOT NULL走索引,设计字段的时候,如果没有要求必须为NULL,那最好给个默认值空字符串。

explain SELECT id from account where province is  NULL

在这里插入图片描述

explain SELECT id from account where province is not NULL

在这里插入图片描述

还有一种情况,单键值的B树索引列上存在null值,导致COUNT(*)不能走索引。

-- status状态加上索引
CREATE INDEX status on account(status)

在这里插入图片描述

explain SELECT count(status) from account

我们来看一下status加上索引,没有为空的数据时,执行计划是啥样的。

在这里插入图片描述

8.联合索引没有遵循最左匹配原则,索引失效

如果使用了联合索引,但查询时未使用索引的第一列,索引也会失效。

原因:比如我们根据字段(t1,t2,t3)建立了联合索引,则排序规则是先按t1字段进行排序,t1字段相同再按t2字段排序,当t1、t2字段都相同时再按t3字段进行排序。如果我们的查询条件中没有使用到第一列,那么该索引也就没有办法使用。

--  创建联合索引
CREATE INDEX idx_phone_provice_status on account(phone,province,status)

我们删除之前加的province和status的单独的索引。

explain select * from account where province = '宝地区' and status = 1

在这里插入图片描述

只要我们把联合索引的第一列放在前面,就可以生效。

explain select * from account where phone = '12384374374' and province = '宝地区' and status = 1

在这里插入图片描述

explain select * from account where phone = '12384374374' and status = 1

在这里插入图片描述

9.不等于操作符(<>、!=)会导致索引失效

这种查询语句无法使用索引,因为需要扫描整个表来查找不等于’value’的记录。

explain select * from account where phone != '12384374374'

在这里插入图片描述

10.IN语句引起的索引失效

使用IN语句进行查询时,如果查询的值列表比较大或者是一个子查询,则会引起索引失效。

我们可以通过以下sql来模拟这种情况,这块就不做真实的演示啦,大家在工作中遇到IN查询的时候可以看一下执行计划,然后做出对应的调整。

SELECT * FROM table_name WHERE column_name IN (SELECT column_name FROM another_table);

这样的查询语句会导致数据库无法使用索引来查找匹配的记录,因为索引只能查找单个值,而不能匹配多个值。

为了避免IN语句导致的索引失效,我们可以使用以下替代方案:

使用EXISTS语句来代替IN语句,例如:

SELECT * FROM table_name1 t1 WHERE EXISTS (SELECT * FROM table_name2 t2 WHERE t2.column_name = t1.column_name);

或者是使用JOIN来代替IN语句,例如:

SELECT * FROM table_name1 t1 JOIN table_name2 t2 ON(t1.column_name = t2.column_name);

11.数据库与表还有表与表的编码不兼容,索引失效

在sql中做表关联时,需要注意两边字段的编码要保持一致。

Ok,以上就是我们在工作中常见的一些索引失效的案例。

接下来我们来说一下,索引的一些设计规则。

  • 高频次查询且数据量大的表建立索引
  • 经常需要排序、分组和联合操作的字段建立索引
  • 短索引可以提升访问的IO效率,对于BLOB、TEXT或很长的varchar列使用前缀索引
  • 删除无用索引,同列上创建多个索引,越多索引维护成本越高,优化器在优化查询时也需要逐个考虑,会影响性能
  • 根据业务需求,设计好联合索引,业务使用的时候尽量用到联合索引,避免回表查询
  • 尽量选择区分度高的列作为索引,区分度越高性能越好,比如唯一索引
  • 索引列不参与计算,带函数的查询不建议做为索引列
  • 尽量扩展利用现有索引,联合索引的查询效率比多个独立索引高
  • 尽量避免NULL,应该指定列为NOT NULL,含有空值的列很难进行查询优化,可以用0或一个空串代替NULL
  • 唯一索引与普通索引
    • 唯一索引和普通索引在性能上没有本质的区别,但在数据的唯一性方面
    • 唯一索引在数据插入和更新时需要更多计算,因此略微慢一些。
  • 聚簇索引与非聚簇索引
    • 聚簇索引在性能上优于非聚簇索引,因为聚簇索引是将数据存储在一起的
    • 这样检索数据时可以最大程度地减少磁盘 IO 操作。但如果经常更新表中的数据,则聚簇索引的维护成本相对较高。
  • 覆盖索引与非覆盖索引
    • 覆盖索引可以直接从索引中获取数据,无需回表查询,因此执行速度更快
    • 但是如果查询需要取出的数据列不在索引中,则无法使用覆盖索引,需要进行回表查询,效率较低。

好啦,至此本文就到这啦,记得三连➕关注哦!

在这里插入图片描述

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

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

相关文章

软件工程——第13章软件项目管理知识点整理(完结)

本专栏是博主个人笔记&#xff0c;主要目的是利用碎片化的时间来记忆软工知识点&#xff0c;特此声明&#xff01; 文章目录 1.管理的定义&#xff1f; 2.软件项目管理地位&#xff1f;&#xff08;重要性&#xff09; 3.软件项目管理过程从一组项目计划活动开始&#xff0c…

raid5故障导致LeftHand存储崩溃的服务器数据恢复案例

HP-LeftHand存储简介&#xff1a; HP LeftHand存储支持RAID5、RAID6、RAID10磁盘阵列&#xff0c;支持卷快照&#xff0c;卷动态扩容等。 服务端&#xff1a; 客户端&#xff1a; LeftHand存储分为三个层级&#xff1a;物理磁盘、逻辑磁盘、逻辑卷。多个物理磁盘组成一个逻辑的…

GPDB-内核特性-gp_interconnect_fc_method参数

GPDB-内核特性-gp_interconnect_fc_method参数 gp_interconnect_fc_method参数控制使用哪种流量控制方式&#xff1a;capacity根据接收方窗口来控制发送&#xff1b;loss(默认)根据丢包情况控制发送速度。Loss是基于capacity&#xff0c;还会根据丢包情况调整发送速度。那么针对…

YOLOv8实战垃圾分类目标检测 (视频课程)

课程链接&#xff1a;https://edu.csdn.net/course/detail/38804 垃圾分类是一项利国利民的民生工程&#xff0c;需要全社会的共同参与。 YOLOv8是前沿的目标检测技术&#xff0c;它基于先前 YOLO 版本在目标检测任务上的成功&#xff0c;进一步提升性能和灵活性。 本课程将手…

基于OpenCV 和 Python 实现车牌检测--附免费源码

文末提供免费的源代码下载链接 车牌检测是使用计算机视觉技术自动检测和识别图像或视频流中的车牌/车牌的过程。 此任务在许多应用中都很有用,例如交通管理、自动收费和停车控制。 车牌检测基本上分为两个任务: 车牌检测:这是指识别图像或视频帧中车牌位置的过程。这涉…

实战:Springboot集成Sentinel实现流量控制、熔断降级、负载保护

文章目录 前言知识积累流量控制负载保护熔断降级官方文档 实战演练部署sentinel-dashboard直接jar包部署docker-compose编排 springboot集成sentinel基础架构搭建sentinel控制台sentinel验证 延伸&#xff1a;系统自适应限流系统规则原理配置页面 写在最后 前言 前面的文章我们…

面试官:请你说下深、浅拷贝并且手写深、浅拷贝,我:你咋知道我只会这个?

前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 一、引言 当我们需要在 JavaScript 中处理对象和数组时&#xff0c;经常需要使用对象和数组的复制功能。JS中有着两种…

TCP 与UDP区别

目录 网络参考模型TCPTCP 是什么特点 UDPUDP 是什么特点 TUP与UDP区别 总结什么时候选TCP or UDP 网络参考模型 TCP TCP 是什么 传输控制协议&#xff08;TCP&#xff09;是TCP/IP模型的传输层协议。它是一个面向连接的协议。因此&#xff0c;协议首先在源和目标之间建立连接…

谈谈对SpringMVC的理解

1、SpringMVC是属于SpringFramework生态里面的一个模块&#xff0c;它是在Servelet基础上构建的&#xff0c;并且使用了MVC模式设计的一个Web框架&#xff1b; 2、它的主要目的是为了简化传统模式下的Serveletjsp的开发模式&#xff0c;其次SpringMVC的架构模式是对于Java的web…

AR 技术应用与管理解决方案:施工建造、机柜扫描、办公室导航

建筑行业作为人类历史上最重要的产业之一&#xff0c;在数字化转型方面同样也在不断推进。图扑软件结合 AR 技术的应用&#xff0c;为建筑行业带来了更加便捷高效的建筑施工过程管理。 传统的建筑施工管理过程中&#xff0c;由于缺乏信息化手段&#xff0c;往往存在资料不全、…

面试算法题—编程题2023

面试算法题—编程题2023 一、冒泡排序二、选择排序三、快速排序四、其它排序 最近在看一些面试题&#xff0c;发现很多面试过程中都会要求手写排序编程题&#xff0c;经过一番查找整理&#xff0c;可以快速学习和使用相关排序算法题&#xff0c;通俗易懂&#xff0c;手撕代码吊…

RFC959 文件传输协议(FTP)翻译

发布时间&#xff1a;2023-07-12 21:14:54 前言 想做一下FTP的项目&#xff0c;带师说要参考RFC做才能标准化&#xff0c;先翻译一下。 官方文档&#xff1a;https://www.rfc-editor.org/rfc/inline-errata/rfc959.html 本备忘录的状态 本备忘录是文件传输协议&#xff08…