MySQL 索引 是如何提高 查询效率 的?

前言

我们都知道当查询数据库变慢时,需要建索引去优化。但是只知道索引能优化显然是不够的,我们更应该知道索引的原理,因为不是加了索引就一定会提升性能。那么接下来就一起探索MYSQL索引的原理吧

什么是索引

索引其实是一种能高效帮助MYSQL获取数据的数据结构,通常保存在磁盘文件中,好比一本书的目录,能加快数据库的查询速度。除此之外,索引是有序的,所以也能提高数据的排序效率。

通常MYSQL的索引包括聚簇索引,覆盖索引,复合索引,唯一索引,普通索引,通常底层是B+树的数据结构。

总结一下,索引的优势在于:

  • 提高查询效率。
  • 降低数据排序的成本。

缺点在于:

  • 索引会占用磁盘空间。
  • 索引会降低更新表的效率。因为在更新数据时,要额外维护索引文件。

索引的类型

  • 聚簇索引

索引列的值必须是唯一的,并且不能为空,一个表只能有一个聚簇索引。

  • 唯一索引

索引列的值是唯一的,值可以为空。

  • 普通索引

没有什么限制,允许在定义索引的列中插入重复值和空值。

  • 复合索引

也叫组合索引,用户可以在多个列上组合建立索引,遵循“最左匹配原则”,在条件允许的情况下使用复合索引可以替代多个单列索引的使用。

索引的数据结构

我们都知道索引的底层数据结构采用的是B+树,但是在讲B+树之前,要先知道B树,因为B+树是在B树上面进行改进优化的。

首先讲一下B树的特点:

  • B树的每个节点都存储了多个元素,每个内节点都有多个分支。
  • 节点中元素包含键值和数据,节点中的键值从小到大排序。
  • 父节点的数据不会出现在子节点中。
  • 所有的叶子节点都在同一层,叶节点具有相同的深度。

img

在上面的B树中,假如我们要找值等于18的数据,查找路径就是磁盘块1->磁盘块3->磁盘块8。

过程如下:

第一次磁盘IO:首先加载磁盘块1到内存中,在内存中遍历比较,因为17<18<50,所以走中间P2,定位到磁盘块3。

第二次磁盘IO:加载磁盘块3到内存,依然是遍历比较,18<25,所以走左边P1,定位到磁盘块8。

第三次磁盘IO:加载磁盘块8到内存,在内存中遍历,18=18,找到18,取出data。

如图所示:

img

如果data存储的是行数据,直接返回,如果存的是磁盘地址则根据磁盘地址到磁盘中取出数据。可以看出B树的查询效率是很高的。

B树存在着什么问题,需要改进优化呢?

第一个问题:B树在范围查询时,性能并不理想。假如要查询13到30之间的数据,查询到13后又要回到根节点再去查询后面的数据,就会产生多次的查询遍历。

第二个问题:因为非叶子节点和叶子节点都会存储数据,所以占用的空间大,一个页可存储的数据量就会变少,树的高度就会变高,磁盘的IO次数就会变多。

基于以上两个问题,就出现了B树的升级版,B+树。 B+树与B树最大的区别在于两点:

  • B+树只有叶子节点存储数据,非叶子节点只存储键值。而B树的非叶子节点和叶子节点都会存储数据。
  • B+树的最底层的叶子节点会形成一个双向有序链表,而B树不会。

如图所示:

img

B+树的等值查询过程是怎么样的?

如果在B+树中进行等值查询,比如查询等于13的数据。

查询路径为:磁盘块1->磁盘块2->磁盘块6。

  • 第一次IO:加载磁盘块1,在内存中遍历比较,13<17,走左边,找到磁盘块2。
  • 第二次IO:加载磁盘块2,在内存中遍历比较,10<13<15,走中间,找到磁盘块6。
  • 第三次IO:加载磁盘块6,依次遍历,找到13=13,取出data。

所以B+树在等值查询的效率是很高的。

B+树的范围查询过程又是怎么样呢?

比如我们要进行范围查询,查询大于5并且小于15的数据。

查询路径为:磁盘块1->磁盘块2->磁盘块5->磁盘块6

  • 第一次IO:加载磁盘块1,比较得出5<17,然后走左边,找到磁盘块2。
  • 第二次IO:加载磁盘块2,比较5<10,然后还是走左边,找到磁盘块5
  • 第三次IO:加载磁盘块5,然后找大于5的数据。
  • 第四次IO:由于最底层是有序的双向链表,所以继续往右遍历即可,直到不符合小于15的数据为止。

过程如图所示:

img

所以在范围查询的时候,是不需要像B树一样,再回到根节点,这就是底层采用双向链表的好处。

所以B+树的优势在于,能保证等值查询和范围查询的快速查找。

InnoDB索引

我们常用的MySQL存储引擎一般是InnoDB,所以接下来讲讲几种不同的索引的底层数据结构,以及查找过程。

聚簇索引

前面讲过,每个InnoDB表有且仅有一个聚簇索引。除此之外,聚簇索引在表的创建有以下几点规则:

  • 在表中,如果定义了主键,InnoDB会将主键索引作为聚簇索引。
  • 如果没有定义主键,则会选择第一个不为NULL的唯一索引列作为聚簇索引。
  • 如果以上两个都没有。InnoDB 会使用一个6 字节长整型的隐式字段 ROWID字段构建聚簇索引。该ROWID字段会在插入新行时自动递增。

除了聚簇索引之外的索引都称为非聚簇索引,区别在于,聚簇索引的叶子节点存储的数据是整行数据,而非聚簇索引存储的是该行的主键值。

比如有一张user表,如图所示:

img

底层的数据结构就像这样:

img

当我们用主键值去查询的时候,查询效率是很快的,因为可以直接返回数据。

img

普通索引

也就是用得最多的一种索引,比如我要为user表的age列创建索引,SQL语句可以这样写:

CREATE INDEX INDEX_USER_AGE ON `user`(age);

普通索引属于非聚簇索引,所以叶子节点存储的是主键值,底层的数据结构大概长这个样子:

img

比如要查询age=33的数据,那么首先查到磁盘块7的age=33的数据,获取到主键值,主键值为4。

接着再通过主键值等于4,查询到该行的数据。所以总得来说,底层会进行两次查询。

这种先通过查询主键值,再通过主键值查询到数据的过程就叫做回表查询。

覆盖索引

既然上面提到了回表查询,那么自然而然会想到,有没有什么办法能避免回表查询呢?答案肯定是有的,那就是使用覆盖索引。

覆盖索引不是一种索引的类型,而是一种使用索引的方式。假设你需要查询的列是建立了索引,查询的结果在索引列上就能获取,那就可以用覆盖索引。

比如上面的例子,我们通过age=33查询,我需要查询的结果就只要age这一列,那就可以用到覆盖索引,如图所示:

img

使用到覆盖索引的话,就能避免回表查询,所以在写SQL语句时尽量不要写SELECT *。

总结

这篇文章主要讲的是索引的类型,索引的数据结构,以及InnoDB表中常用的几种索引。

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

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

相关文章

FreeMarker详细介绍

FreeMarker 1. 主要内容 2.FreeMarker概述 2.1. FreeMarker概念 FreeMarker 是一款 模板引擎&#xff1a; 即一种基于模板和要改变的数据&#xff0c; 并用来生成输出文本(HTML网页&#xff0c;电子邮件&#xff0c;配置文件&#xff0c;源代码等)的通用工具。 是一个Java类…

SpringBoot整合Freemarker导出word文档表格

freemarker模板里面的template.process()方法里传入的第一个参数Object类型&#xff0c;如果是一个实体类对象在模板上怎么进行渲染&#xff0c;将实体类的值取出 freemarker会调用ObjectWrapper对传入的对象进行warp&#xff0c;具体类型在代码里面用instanceof进行判断。一般…

ambari 修改服务器名,Ambari修改主页面方法

分享下Ambari修改主页面方法&#xff0c;希望对大家有用。[roothdp159 ambari-web]# brunch watch --serverOct 10:22:43 - info: application started on http://localhost:3333/Oct 10:22:47 - info: compiled 891 files into 5 files, copied 260 in 3988msOct 10:23:12 - i…

快速向服务器传文件格式,客户端如何向服务器传文件格式

客户端如何向服务器传文件格式 内容精选换一换语音通话平台通过此接口向客户推送语音通话业务用户呼叫时的状态信息&#xff0c;如呼入、呼出、振铃、应答、挂机等状态的信息。语音通话平台(客户端) → 客户服务器(服务端)前提条件SP在开发应用时&#xff0c;若需订阅呼叫状态通…

MyBatis-Plus--解决逻辑删除与唯一索引的问题--方法/实例

文章目录简介问题复现依赖**建库建表**代码测试解决方案方案1&#xff1a;将字段设置为id&#xff08;推荐&#xff09;建库建表修改Entity测试方案2&#xff1a;将字段设置为当前时间&#xff08;不推荐&#xff09;建库建表修改Entity测试简介 说明 本文用示例介绍MyBatis-…

MybatisPlus 实体类与数据库表映射关系MybatisPlus:ORM思想

实体类与数据库表映射关系 使用mybatisPlus时&#xff0c;会确定实体类和数据的映射关系 具体的映射方法有两种 1、默认&#xff1a;采用驼峰映射规则 例如MyUserTable 对应的数据库表为 my_user_table ; TEMyUserTable 对应表名为t_e_my_user_table; 2、注解TableName 在…

Spring Boot——maven项目常用打包配置

文章目录一、简介二、pom.xml三、效果图3.1 所有的资源打包到指定的目录maven3.2 所有的配置文件都放到config目录3.3 所有的配置文件都不在jar里&#xff0c;防止敏感信息泄露结语一、简介 maven项目打包是我们程序员经常遇到的事&#xff0c;今天就来弄一个常用的打包方式&a…

史上最全ThreadLocal 详解

文章目录一、ThreadLocal简介二、ThreadLocal与Synchronized的区别三、ThreadLocal的简单使用四、ThreadLocal的原理4.1 ThreadLocal的set()方法&#xff1a;4.2 ThreadLocal的get方法4.3 ThreadLocal的remove方法4.4、ThreadLocal与Thread&#xff0c;ThreadLocalMap之间的关系…

Java递归构建树形结构

记录&#xff1a;在Java后台利用递归思路进行构建树形结构数据&#xff0c;返回给前端&#xff0c;能以下拉菜单等形式进行展示。 简明&#xff1a;为了简化代码&#xff0c;引入Lombok的Jar包&#xff0c;可省略实体类set()、get()方法。 <dependency><groupId>or…

HTTP状态码含义:428、429、431、511431状态码详解

1、428 Precondition Required (要求先决条件) ​ 先决条件是客户端发送 HTTP 请求时&#xff0c;必须要满足的一些预设条件。一个好的例子就是 If-None-Match 头&#xff0c;经常用在 GET 请求中。如果指定了 If-None-Match &#xff0c;那么客户端只在响应中的 ETag 改变后才…

Java 异常——Exception详解

异常的介绍 异常的概念 异常 &#xff1a;指的是程序在执行过程中&#xff0c;出现的非正常的情况&#xff0c;最终会导致JVM的非正常停止。 在Java等面向对象的编程语言中&#xff0c;异常本身是一个类&#xff0c;产生异常就是创建异常对象并抛出了一个异常对象。Java处理…

Java中的URL类根据url获取网络文件快速入门Java中的URL(网络编程)

Java中的URL类 远程连接来实现应用。而且&#xff0c;这个平台现在已经可 以对国际互联网以及URL资源进行访问了。Java的URL类可以让访问网络资源就像是访问你本地的文件夹一样方便快捷。我们通过使用Java的URL类 就可以经由URL完成读取和修改数据的操作。 通过一个URL连接&a…

SpringMVC中Controller为什么能够处理并发访问?Springboot中的定时任务是否会发生阻塞?

文章目录SpringMVC中Controller为什么能够处理并发访问&#xff1f;当多个请求同时访问服务器的时候Controller、Service、DAO是线程安全的吗&#xff1f;关于类中的变量Controller、Service、DAO等类都默认为单例模式Controller、Service、DAO等类中的方法当中的并发问题关于D…

Java进阶 - 易错知识点整理

转载&#xff1a;https://blog.csdn.net/qq_33934427/article/details/125903960 文章目录1、JavaEE2、网络基础3、Mysql4、Spring/SpringMVC&#xff08;IOC装配、AOP增强、常用注解&#xff09;5、Spring Boot/Spring Cloud1&#xff09;SpringBoot部分2&#xff09;SpringCl…

MySQL 视图(详解) navicat如何创建视图

文章目录MySQL 视图&#xff08;详解一&#xff0c;视图概念使用视图的原因二&#xff0c;创建视图&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;创建基于单表的视图【实例 1】【实例 2】&#xff08;3&#xff09;创建基于多表的视图【实例 3】&#xff08;4&a…

使用set集合去除重复元素@EqualsAndHashCode注解

如何使用set集合去重 ​ 我们都知道&#xff0c;set集合是无序的&#xff0c;这样也导致set集合里面的元素是不能重复的&#xff0c;因为这一个特性&#xff0c;所以我们经常用set集合进行去重操作&#xff0c;我们下面以一个简单的例子说明set集合是如何进行去重的。 创建去…

缺少构造方法:Cause java.sql.SQLDataException Unsupported conversion from LONG to java.sql.Timestamp

今天遇到了一个奇怪的错误&#xff0c;报错如下图所示&#xff1a; org.springframework.dao.DataIntegrityViolationException: Error attempting to get column question_id from result set. Cause: java.sql.SQLDataException: Unsupported conversion from LONG to java…

SpringBoot瘦身打包部署

一、前言 最近做的项目由于引入第三方库导致在运行mvn clean package 打jar时&#xff0c;编译出来的 Jar 包很大&#xff08;服务器多达500MB&#xff09;。 二、瘦身前的Jar包 SpringBoot编译出来的Jar包中&#xff0c;磁盘占用大的&#xff0c;是一些外部依赖库&#xff…

XShell直接拖拽文件到服务器,不使用Xftp等文件上传工具

很多情况下&#xff0c;我们使用 Xshell 工具时&#xff0c;如果遇到文件的上传和下载会不可避免的要用到另外一个工具 Xftp&#xff0c;但是频繁的使用 Xftp 会比较麻烦&#xff0c;那么有没有一种更加直接简单的方法呢&#xff1f; 当我们所需要上传的文件比较小的时候&…

System.getProperty()方法获取系统变量

今天在阅读JDBC的DriverManager类源码时&#xff0c;看到了这么一句代码&#xff1a; System.getProperty(“jdbc.drivers”)&#xff1b;getProperty()这个方法是获取指定键指示的系统属性的&#xff0c;也就是说上面的代码获取的是jdbc.drivers这个属性。我写了个测试测试输…