一、了解[mysql]索引底层结构和算法

目录

  • 一、索引
    • 1.索引的本质
    • 2.mysql的索引结构
  • 二、存储引擎
    • 1.MyISAM
    • 2.InnoDB
    • 3.为什么建议InnoDB表要建立主键并且推荐int类型自增?
    • 4.innodb的主键索引和非主键索引(二级索引)区别
    • 5.联合索引

一、索引

1.索引的本质

索引:帮助mysql高效获取数据并排好序的数据结构

简单举例,我们把一串数据保存到mysql表中的格式如下。
mysql数据保存到磁盘时,每条数据保存的位置并不一定是连续的磁盘地址进行保存的,即mysql表中相邻的两条数据,对应的磁盘地址不一定相邻。
在这里插入图片描述

执行一条查询语句

SELECT * FROM TABLE WHERE TABLE.column2 = 15 

    在没有索引的情况下,他是在全部表数据的范围内开始查找了,从第一条column2为20开始逐条向下找,直到找到15为止。每次查询一条记录时就去和磁盘做一次I/O操作,把存在磁盘中的数据读取出来,那么如果数据量很大时,需要磁盘I/O的次数可能会非常的多,性能不高并且效率非常的低。
    基于这种场景,我们就可以设计一个简单的索引,来解决这种问题。把这些数据保存到一个二叉树中(mysql并没有使用二叉树作为索引存储结构这里只是举例)。
二叉树中,每个根节点大于左子节点,小于右子节点。
    按照这个规律,查找15时,先比较根节点20,15<20,那么去根节点的左边继续查找,节点15,15等于15。查找两次就找到了。
在这里插入图片描述

2.mysql的索引结构

    那么mysql为什么没有采用二叉树呢?
    我们这时来看一下表中第一列的数据,它从1开始值是连续递增的。按照上边的逻辑节点大于左子节点小于右子节点的值。
数据结构演示网站
在这里插入图片描述
    这样就变成了一个链表了,此时查找某个值的时候,又变成全表扫描了。
    那能不能改进成红黑树呢?
在这里插入图片描述
    那虽然不会变成链表,但是实际情况下,mysql表中保存的数据不可能就只有几条数据,可能有几万,几十万甚至更多的数据,此时使用红黑树那么树的高度可能会非常的高。如果有100w数据,假设树的高度为50层,如果要查找的数据在这颗树的叶子节点,那么需要查找的次数就为50次。所以说在数据量特别大的情况下红黑树解决的效果就非常的局限了。数越高需要查找的次数就越多,即I/O次数就越多,效率就越低。

    那能不能控制一树的高度,不要让他这么高呢?
    那就有必要稍微了解一下B树结构了。百度百科
=》了解完B树,这里再来举个例子。《=

在这里插入图片描述
一个节点指定是:
在这里插入图片描述

上图中,每个节点都是按照从左到右值依次递增的,所有的元素不会重复,叶节点具有相同深度,叶节点的指针为空。假设深绿色有数值的部分代表mysql表中第一列的值把他理解成id列,下方data保存了它所有对应的数据,即一个id对应的一行数据的所有内容,拿15来举例,15下的data部分a保存的是它15这个值下对应的相关信息。这种数据结构就解决了红黑树的树高度问题,但是实际上mysql也没用采用这种数据结构。而是对它进行了改造,也就是B+树。

=》B+树举例。《=

在这里插入图片描述
B+树:非叶子节点不存data,只存索引(冗余数据),这样可以放更多的索引。叶子节点包含所有的索引字段。叶子节点用指针连接,提高区间访问的性能。

B+树每个节点中的值,拿下图举例,当二叉树来理解,即20这个节点做子树的值都是小于20,右子树的值,都是大于等于20。
在这里插入图片描述
B+树的叶子节点相邻节点直接是有指针相连的,并且值从左往右依次递增的,也就是排好序的。
在这里插入图片描述
B+树中的一个节点其实就是mysql中一页。mysql中默认一页数据的大小为16KB。(这个大小可以改,但是不建议去改,mysql为啥要定16它肯定是经过理论测试等等过程之后得出来的,是符合大多数场景的。)
在这里插入图片描述
这时,淡绿色代表的是下一页数据的地址。
在这里插入图片描述
然后我们要查找一个值的时候,比如50,mysql每次会把一页数据加载到内存,因为一页数据即B+树的一个节点,节点里的值都是排好序的,所以可以通过查找算法来进行查找,例如通过二分查找算法去查找,最终找到或者未找到。找到50时,这里的data可能保存的是这个记录对应的所有行数据,也可能是一个地址(这个其实取决于索引的类型和存储引擎这里说的是innodb引擎,下边会讲到)。

这时,我们就能大概算出每个节点能保存多少数据了。一页数据中是由具体的数据和下一页数据地址为单位组成的。
在这里插入图片描述
假设15为int类型,mysql中int类型占4个字节的话,下一页地址占大约6个字节。
16KB * 1024 = 16384字节。
16384 / (4+6) ≈ 1638
所以保存int类型的数据,一页大概能保存1638个。如果B+树为三层,那么能保存多少数据呢?
叶子节点一页16Kb一页数据,里边放了不止int这个值,还有data数据。叶子节点每个估计能放16条数据的话。总共能放多少数据呢?
1638 * 1638 * 16 = 42928704 (4千多万)。4千多万树的高度才三层。大大减少 了I/O操作次数。
所以说如果查找数据时走索引查找数据效率是非常高的,而且根节点中的数据是常驻内存中的。

为什么mysql在B和B+树中,最终选择B+树?
如果B树保存int类型的数据。因为B树每个节点下每个int对应的数据是都保存起来的。按照一个节点16Kb大小,一个节点能保存16条数据来算的话,保存4千多万的数据,最终B树能多高呢?
16 * 16 * 16 * …=4千万。
需要乘多少个16,就有少层的高度,结果显而易见。

而B+树的树的高度,取决于非叶子节点每个节点能放多少索引数据(多少个int值),放的越多高度就会越低。所以为什么只有叶子节点存data。其它存的只是索引数据。

当然mysql不止这一种数据结构的索引还有HASH类型的索引。


二、存储引擎

1.MyISAM

MyISAM索引文件和数据文件是分离的(非聚集),请看下面讲解。

我们知道mysql中的数据是存在磁盘中,那么具体在哪个地方呢?
我们有一个数据库:study_mysql
在这里插入图片描述
库中有两个表,其中myisam_table存储引擎为MyISAM

CREATE TABLE `myisam_table` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

如果没有修改mysql配置的话,默认是把这些数据库和表保存到data文件夹下的。
在这里插入图片描述
下图三个文件对应myisam_table表相关文件。
在这里插入图片描述
其中

  • .frm保存的是表的结构数据。
  • .MYD保存的表数据。
  • .MYI保存的表索引数据。

=》MyISAM数据保存和查询举例。《=
在这里插入图片描述

数据也就是下方表格的内容,保存在.MYD文件中。
在这里插入图片描述

索引也就是B+树的数据保存在.MYI文件中。

在这里插入图片描述
如果要查询列1的值等于30的内容,那么首先去.MYI文件中查找30,然后在根据叶子节点保存的内存地址,再去.MYD文件找到他对应的数据。

非聚集索引:简单理解为叶子节点没有保存索引对应的所有数据内容。请结合下方innodb理解。

2.InnoDB

了解MyISAM的数据保存和读取之后,对于非聚集的概念应该多少有点理解了。
表设置为innodb引擎:
还是study_mysql库

库中有两个表,其中innodb_table存储引擎为InnoDB

CREATE TABLE `innodb_table` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

在这里插入图片描述
其中

  • .frm保存的是表的结构数据。
  • .ibd保存的是索引和数据。

在这里插入图片描述
表数据文件本身就是按照B+树组织的一个索引结构文件
聚集索引(聚簇索引,主键索引):叶子节点本身包含了完整的数据记录。
之前有说过叶子节点可能保存的不是所有数据,可能是数据所对应的磁盘地址。其实innodb非主键索引,叶子节点保存得是主键内容,如果叶子节点保存的是全部数据那么就是聚集索引。反之叶子节点只包含主键数据的,称为非聚集索引。MyISAM的主键索引和非主键索引都是一样的。

3.为什么建议InnoDB表要建立主键并且推荐int类型自增?

    如果创建表时指定了主键,那么mysql在构造B+树时就可以根据你的主键去维护这个B+树的结构。如果你没指定主键,那么mysql就会从你表中的所有列中找到一列,这列中的值都不重复,如果没有找到则它会自己创建一个隐藏的列,这个隐藏的列会和你表中的数据对应起来,使用隐藏列去构造这个B+树。

那你可能有几个疑问:
    1.为啥mysql能做,我还要自己去建主键呢?
    其实你这么理解本来干一个事情,你事先准备好沟通好开始去干,可能就只需要等2小时。同一件事你懒得去沟通,导致后续需要额外的1小时来处理你预期想要的东西,总共用了3小时。现在无论是企业还是个人都注重效率,那么为啥不能一个主键能解决的事情还要让程序额外帮你搞呢?当然针对是这个场景。其他情况可能就是得需要程序搞来提高我们的效率,不要过度理解。
    2.为啥推荐int类型呢?
    你想想你学过的查找算法,大部分场景是不是都是数字之间的比较。如果是字符串之间比较,你还需要把字符串每位的ASCII码值计算出来,最后根据整体的值比大小,这又和上边说的有点像了,本来2小时能完成,你非要干3小时。虽然性能影响不会非常大,不然mysql估计也不会让你设置成字符串类型。当然这里还是推荐int,如果说非要有个场景就需要字符串类型,那就是你把控了,决定权在你。
3.为啥要自增呢?
演示地址
还是那个网站,你们可以自己感受一下,插入连续递增的数据和插入有大有小的数据,不是递增的数据。看下一这个过程。
在这里插入图片描述
这个过程,你可以看出,如果是自增的话,它只需要在最后一个节点插入,放不下的时候,在后边追加一个节点,并且维护一下上一个节点的值。如果不是自增的话,你往3和4中间插入一个3.5那么就不止是后边追加,可能要分裂还要平衡等。效率是没有自增的高。

4.innodb的主键索引和非主键索引(二级索引)区别

innodb的表,每张表只有一个主键索引,就是叶子节点存放了所有数据内容的索引。
非主键索引,叶子节点只放了对应的主键内容。根据指导的字段建立的索引,比如根据名称列创建的索引。
通过name查询时,走的二级索引,最终找到name对应的主键就,然后再去主键索引里找到他对应的这一行所有数据。也就是我们常说的回表,就是通过二级索引找到id,然后在去根据id查询对应的内容。(这里举例是单值索引,就是一个字段建立一个索引,不推荐建立很多单值索引,这里只是举例。)
在这里插入图片描述

为什么二级索引只存主键信息呢?为啥不把数据全放在叶子节点呢?

  1. 节省空间,如果你的表数据很多,又多个二级索引那么这么多重复的数据是非常占空间的。
  2. 保持一致性,你插入一条数据所有的索引都需要维护数据,如果只有主键索引维护,减少了部分维护成本

5.联合索引

1.联合主键结构
可以理解为有一张雇员表,有name,age,job,join_time这几列,其中name,age,job作为联合主键。
在这里插入图片描述
那么叶子节点就是存的join_tiem的数据。

2.联合索引结构(非联合主键)
如果是根据列 name,age,job创建的二级索引,那么结构如下。叶子节点的值为主键信息。
在这里插入图片描述
这里有一个最左前缀的内容。
即你创建索引的时候

KEY `idx_name_age_job` (`name`,`age`,`job`)

根据你指定的字段,优先级按照你指定列的左边第一列开始,先按照name排序,如果值相同,按照第二列age排序,以此类推。也就是上图的内。

=》索引最左前缀举例《=

1. 
select * from table where name = 'Tom' and age = 19
2.
select * from table where age = 19
3.
select * from table where age = 19 and job = 'dev'
4.
select * from table where job = 'dev'

上述4条语句那些会走索引,那些不会走索引。(这里的索引时上图中的联合索引)
结果:只有第一条会走索引。
为什么只有第一条走索引呢?
因为你的索引创建规则是按照name,age,job的优先级创建的,
首先,我根据name能找到一个范围,这个范围里的数据都是Tom的。
其次,在这个范围中,age也是有序的,所以在这个范围进一步跟进age进行筛查,把范围进一步缩小。
最后,缩小之后的范围就是我们要的全部数据了。

其他情况:
拿2.select * from table where age = 19举例。
你直接查age=19。
此时age在索引中保存的是无序的。为什么这么说呢?你明明说的是根据优先级排序的啊,age的优先级排第二。这个是没错的,那他这age是不是相对于name这个字段是有序的,但是思考一下基于全表他还有序吗?
在这里插入图片描述
结果显而易见,基于全表是无序的,基于name是有序的。所以只能去表中一个一个找,不会走这个索引的。其他的sql也是这个道理。你没办法一步一步的缩小范围,或者你一开始就没办法限定一个范围。那么你就只能全表扫描,那就不走索引了。


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

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

相关文章

C++------vector【STL】

文章目录 vector的介绍及使用vector的介绍vector的使用 vector的模拟实现 vector的介绍及使用 vector的介绍 1、vector是表示可变大小数组的序列容器。 2、就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问和数…

【前端自动化部署】,Devops,CI/CD

DevOps 提到Jenkins&#xff0c;想到的第一个概念就是 CI/CD 在这之前应该再了解一个概念。 DevOps Development 和 Operations 的组合&#xff0c;是一种方法论&#xff0c;并不特指某种技术或者工具。DevOps 是一种重视 Dev 开发人员和 Ops 运维人员之间沟通、协作的流程。…

第 3 章 栈和队列(单链队列)

1. 背景说明 队列(queue)是一种先进先出(first in first out,缩为 FIFO)的线性表。它只允许在表的一端进行插入&#xff0c;而在另一端删除元素。 2. 示例代码 1&#xff09;status.h /* DataStructure 预定义常量和类型头文件 */#ifndef STATUS_H #define STATUS_H/* 函数结果…

【大数据】Flink 详解(六):源码篇 Ⅰ

Flink 详解&#xff08;六&#xff09;&#xff1a;源码篇 Ⅰ 55、Flink 作业的提交流程&#xff1f;56、Flink 作业提交分为几种方式&#xff1f;57、Flink JobGraph 是在什么时候生成的&#xff1f;58、那在 JobGraph 提交集群之前都经历哪些过程&#xff1f;59、看你提到 Pi…

【个人博客系统网站】统一处理 · 拦截器

【JavaEE】进阶 个人博客系统&#xff08;2&#xff09; 文章目录 【JavaEE】进阶 个人博客系统&#xff08;2&#xff09;1. 统一返回格式处理1.1 统一返回类common.CommonResult1.2 统一返回处理器component.ResponseAdvice 2. 统一异常处理3. 拦截器实现3.1 全局变量SESSI…

Kitchen Hook

双扛厨房排钩&#xff1a;挂刀具

低代码赋能| 绿色智慧矿山解决方案

在世界能源日趋紧张的背景下&#xff0c;能源产业的数字化升级是大势所趋。矿山行业作为国家能源安全的“压舱石”&#xff0c;也必须进行产业升级。一直以来&#xff0c;国家都在大力推动智慧矿山建设。通过大数据、GIS、物联网、云计算、人工智能等新兴技术&#xff0c;实现矿…

Swift 技术 视频播放器滚动条(源码)

一直觉得自己写的不是技术&#xff0c;而是情怀&#xff0c;一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的&#xff0c;希望我的这条路能让你们少走弯路&#xff0c;希望我能帮你们抹去知识的蒙尘&#xff0c;希望我能帮你们理清知识的脉络&#xff0…

云计算 - 百度AIStudio使用小结

云计算 - 百度AIStudio使用小结 前言 本文以ffmpeg处理视频为例&#xff0c;小结一下AI Studio的使用体验及一些避坑技巧。 算力获得 免费的算力获得方式为&#xff1a;每日登录后运行一个项目&#xff08;只需要点击运行&#xff0c;不需要真正运行&#xff09;即可获得8小…

深入理解 JVM 之——动手编译 JDK

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 本篇为深入理解 Java 虚拟机第一章的实战内容&#xff0c;推荐在学习前先掌握基础的 Linux 操作、编译原理基础以及扎实的 C/C 功底。 该系列的 GitHub 仓库&#xff1a;https://github.com/Doge2077/lear…

Python爬虫网络安全:优劣势和适用范围分析

目录 优势和劣势 优势&#xff1a; 劣势&#xff1a; 适用范围&#xff1a; 1. 网页数据收集和分析&#xff1a; 2. 漏洞扫描和安全评估&#xff1a; 3. 威胁情报收集&#xff1a; 4. 社交媒体监测和情感分析&#xff1a; 注意事项 1. 合规性和法律规定&#xff1a; …

VB.NET 如何将某个Excel的工作表中复制到另一个的Excel中的工作表中https://bbs.csdn.net/topics/392861034

参考http://share.freesion.com/306372/可以实现直接拷贝指定表 Private Sub Excel复制工作簿()Dim myExcelApp As New Microsoft.Office.Interop.Excel.ApplicationmyExcelApp.Workbooks.Open(System.Environment.CurrentDirectory "\\测试用例.xlsx", Type.Missin…

深入浅出理解Allan方差分析方法

一、参考资料 深入浅出理解卡尔曼滤波 二、Allan方差分析方法 1. 引言 传统的误差指标往往是采用均值误差&#xff08;反映整个误差序列有无宏观偏置&#xff09;、标准差&#xff08;反映整个误差序列的波动情况&#xff09;&#xff0c;以及均方根&#xff08;RMS&#x…

怎样来实现流量削峰方案

削峰从本质上来说就是更多地延缓用户请求&#xff0c;以及层层过滤用户的访问需求&#xff0c;遵从“最后落地到数据库的请求数要尽量少”的原则。 1.消息队列解决削峰 要对流量进行削峰&#xff0c;最容易想到的解决方案就是用消息队列来缓冲瞬时流量&#xff0c;把同步的直…

uview ui 1.x ActonSheet项太多,设置滚动(亲测有效)

问题&#xff1a;ActionSheet滚动不了。 使用uview ui &#xff1a;u-action-sheet, 但是item太多&#xff0c;超出屏幕了&#xff0c; 查了一下文档&#xff0c;并没有设置滚动的地方。 官方文档&#xff1a;ActionSheet 操作菜单 | uView - 多平台快速开发的UI框架 - uni-a…

Spring Cloud--从零开始搭建微服务基础环境【三】

&#x1f600;前言 本篇博文是关于Spring Cloud–从零开始搭建微服务基础环境【三】&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;…

使用Fiddler模拟网络

Fiddler已经预置提供了模拟Modem速度的选项&#xff0c;其位置位于&#xff1a; Rules->Performances->Simulate Modem Speeds 勾选该选项后&#xff0c;所有通过Fiddler代理的流量都会变得用56k modem上网一般。 要直观观察限速后的效果&#xff0c;最好使用运行在浏览…

Linux用一键安装包部署禅道(18.5版本)

一、安装 禅道软件下载地址&#xff1a;禅道官方下载地址 - 禅道开源项目管理软件 - 禅道开源项目管理软件 请根据自己的需要下载对应的版本。 官方教程地址: (推荐)Linux用一键安装包 - 禅道使用手册 - 禅道开源项目管理软件 注&#xff1a;Linux 一键安装包必须直接解压到 …

2023-9-3 分解质因数

题目链接&#xff1a;分解质因数 #include <iostream>using namespace std;void divide(int n) {for(int i 2; i < n / i; i ){if(n % i 0){int res 0;while(n % i 0){n / i;res ;}cout << i << << res << endl;}}if(n > 1) cout &l…

linux 内存一致性

linux 出现内存一致性的场景 1、编译器优化 &#xff0c;代码上下没有关联的时候&#xff0c;因为编译优化&#xff0c;会有执行执行顺序不一致的问题&#xff08;多核单核都会出现&#xff09; 2、多核cpu乱序执行&#xff0c;cpu的乱序执行导致内存不一致&#xff08;多核出…