Mysql底层原理四:B+树索引

news2024/11/27 0:19:37

B+树索引(索引的原理)

1.前言

前边我们详细唠叨了InnoDB数据⻚的7个组成部分,知道了各个数据⻚可以组成⼀个双向链表,⽽每个数据⻚中的记录会按照主键值从⼩到⼤的顺序组成⼀个单向链 表,每个数据⻚都会为存储在它⾥边⼉的记录⽣成⼀个⻚⽬录,在通过主键查找某条记录的时候可以在⻚⽬录中使⽤⼆分法快速定位到对应的槽,然后再遍历该槽 对应分组中的记录即可快速找到指定的记录(如果你对这段话有⼀丁点⼉疑惑,那么接下来的部分不适合你,返回去看⼀下数据⻚结构吧)。⻚和记录的关系示 意图如下:

2.没有索引的查找

2.1 在一个页中查找

  • 以主键为搜索条件
  • 以其他列为搜索条件
    对⾮主键列的查找的过程可就不这么幸运了,因为在数据⻚中并没有对⾮主键列建⽴所谓的⻚⽬录,所以我们⽆法通过⼆分法快速定位相应的槽。这种情况下 只能从最⼩记录开始依次遍历单链表中的每条记录,然后对⽐每条记录是不是符合搜索条件。很显然,这种查找的效率是⾮常低的。

2.2 在很多个页中查找

在很多页的时候,我们应该是先定位到对应的页,然后到页中根据槽来查找。但是在没有索引的情况下,由于我们并不能快速定位到记录所在的页,所以只能从第一个页沿着链表遍历所有的页,是不是超级超级蠢。

3.一个简单的索引方案

问题:为什么我们要遍历所有的数据页拿?

因为各个页之间没有规律,我们并不知道怎么去匹配记录,所以不得不遍历所有的数据页。如果我们想快速定位到需要查找的记录该怎么办,其实参照记录目录的方法,我们是不是也可以建立一个有序的的目录🙏,建立这个目录必须完成以下这些事:

  1. 下一个页的记录主键值必须大于 上一个页记录的主键值
  2. 页分裂(一个页放不下的时候,分成两个页)时,依旧要保证上面说的关系。

1)准备工作

建表:

mysql>	CREATE	TABLE	index_demo(
   	->    	c1	INT,
   	->    	c2	INT,
   	->    	c3	CHAR(1),
   	->    	PRIMARY	KEY(c1)
   	->	)	ROW_FORMAT	=	Compact;
Query	OK,	0	rows	affected	(0.03	sec)

插入数据:

mysql>	INSERT	INTO	index_demo	VALUES(1,	4,	'u'),	(3,	9,	'd'),	(5,	3,	'y');
Query	OK,	3	rows	affected	(0.01	sec)
Records:	3 	Duplicates:	0 	Warnings:	0

这时候的页如图所示:

2)插入数据测试

再插入一条记录:

mysql>	INSERT	INTO	index_demo	VALUES(4,	4,	'a');
Query	OK,	1	row	affected	(0.00	sec)

因为页10只能放3个记录,所以我们不能不再分配一个页:

问题:为什么页号不是连续的?

新分配的数据页编号可能并不是连续的,也就是我们使用的页空间上并不是连续的。它们只是通过维护上下页来建立链表关系。另外,⻚10中⽤户记录最⼤的主键值是5,⽽⻚28中有⼀条记录的主键值是4,因为5 > 4,所 以这就不符合下⼀个数据⻚中⽤户记录的主键值必须⼤于上⼀个⻚中⽤户记录的主键值的要求,所以在插⼊主键值为4的记录的时候需要伴随着⼀次记录移动,也 就是把主键值为5的记录移动到⻚28中,然后再把主键值为4的记录插⼊到⻚10中,这个过程的示意图如下:

3)  给页建目录

以页28为例,它对应目录项2,这个目录项包含主键值,页号。比方说我们想查找主键值为20的记录,具体步骤粪两步:

  1. 先从⽬录项中根据⼆分法快速确定出主键值为20的记录在⽬录项3中(因为 12 < 20 < 209),它对应的⻚是⻚9。
  2. 再根据前边说的在⻚中查找记录的⽅式去⻚9中定位具体的记录。

记住,这个目录有个别名,就是索引,哈哈

4. InnoDB的索引方案

4.1  上述的索引方案有什么问题?

  1. 当表中数据不断增多的时候,目录项非常耗费空间,这对于记录数比较多的表不太现实
  2. 我们时常会对记录做增删,假设我们把页28中的记录全部删除了,页28也就没有必要存在 ,同时目录项2也没有存在的必要了。

设计InnoDB的大叔们灵光乍现,发现这些目录项其实和数据页长的差不多,只不过目录项存储的列值时主键和页号,所以他们复用了数据页的结构来存储目录项,为了区分,利用 头信息的record_type属性。 它的各个取值代表意思如下:

  • 0:普通记录
  • 1:目录项
  • 2:最小记录
  • 3:最大记录

4.2 最终实现的样子

从图中可以看出,我们新分配了一个编号为30的页用来专门存储目录项记录。这里再次强调一遍 目录项记录和普通的 用户记录的不同点:

  • 目录项记录的record_type值是1,而普通用户记录的record_type值是0。
  • 目录项记录只有主键值和页的编号两个列,而普通的用户记录是用户自己定义的,可能包含很多列,另外还有InnoDB自己添加的隐藏列。
  • 还记得我们之前在唠叨记录头信息的时候说过一个 min_rec_mask的属性么,只有在存储目录项的页中的**主键值的目录项记录的min_rec_mask值为1,**其他都是0

假设我们按照某个主键20去查找记录,大致步骤分以下几步:

  1. 先看页的目录,也就是30的页通过二分法(这里指的是槽)找到对应的目录项记录,因为12<20<209,所以定位到页9。
  2. 再到页9中,根据二分法快速定位到对应的用户记录。

问题:虽然说目录项记录中只存储主键值和对应的页号,比用户记录需要的空间存储小多了,但是一个页也就16kb,能存放的目录项记录也是有限的,那如果表中的记录数据太多,以至于一个数据页不足以存放所有的目录项记录,该怎么办?

4.3 当一个页目录不够的时候怎么办

当然是再准备一个目录页啦,~ 为了⼤家更好的理解新分配⼀个⽬录项记录⻚的过程,我们假设⼀个存储⽬录项记录的⻚最多只能存放4条⽬录项记录(请 注意是假设哦,真实情况下可以存放好多条的),所以如果此时我们再向上图中插⼊⼀条主键值为320的⽤户记录的话,那就需要分配⼀个新的存储⽬录项记录的⻚喽:

从图中可以看出,我们插入一个主键值为320的时候需要两个数据页:

  • 为存储该记录,新增页31
  • 因为原先存储的目录项记录30已经满了,所以新生成一个目录项页32

假设我们还是要查找主键为20的记录,大致步骤分以下几步:

  1. 确定目录项记录页(先确定记录在哪个目录页内),我们现在的存储⽬录项记录的⻚有两个,即⻚30和⻚32,⼜因为⻚30表示的⽬录项的主键值的范围是[1, 320),⻚32表示的⽬录项的主键值不⼩于320,所以 主键值为20的记录对应的⽬录项记录在⻚30中。
  2. 确定记录所在页(确定真实记录在哪个页内),在一个存储目录项记录的页中通过主键值二分法定位的一条目录项记录
  3. 在数据页中查找主键为20的记录

4.4 当一层页目录越来越多时怎么办

问题:如果数据量特别大,目录页就会越来越多,查询性能就会低下,这时候怎么办?

给页目录再生成一个目录,就像这样:

如图,我们⽣成了⼀个存储更⾼级⽬录项的⻚33,这个⻚中的两条记录分别代表⻚30和⻚32,如果⽤户记录的主键值在[1, 320)之间,则到⻚30中查找更详细的⽬ 录项记录,如果主键值不⼩于320的话,就到⻚32中查找更详细的⽬录项记录。

如果画的更加好一点,最终的样子是这样的:

这玩意是不是长得特别像倒过来的数呀,上头是树根,下头是树叶,我们称之为 B+数

无论是存放用户记录的数据页还是存放目录像记录的数据页,都放到这个B+数结构中了,所以我们都称之为节点。**从图中我们可以看出用户记录时存放到B+树最底层的节点上,我们称之为叶子节点。**其余用来存放目录项的节点称之为非叶子节点。

存储能力:

4.5 常说的聚簇索引是什么

我们上边介绍的B+树本身就是一个目录,或者说本身就是一个索引,它有两个特点:

  1. 使用记录主键值的大小进行记录和页的排序,这包括以下三个方面的含义:
    • ⻚内的记录是按照主键的⼤⼩顺序排成⼀个单向链表。
    • 各个存放⽤户记录的⻚也是根据⻚中⽤户记录的主键⼤⼩顺序排成⼀个双向链表。
    • 存放⽬录项记录的⻚分为不同的层次,在同⼀层次中的⻚也是根据⻚中⽬录项记录的主键⼤⼩顺序排成⼀个双向链表。
  2. B+树的叶子节点存储的是完整的用户记录(这个记录中存储了所有列的值

我们把具有这两种特性B+树称之为聚簇索引 。所有完整的⽤户记录都存放在这个聚簇索引的叶⼦节点处。这种聚簇索引并不需要我们在MySQL语句中显式的使⽤INDEX 语句去创建(后边会介绍索引相关的语句),InnoDB存储引擎会⾃动的为我们创建聚簇索引。另外有趣的⼀点是,在InnoDB存储引擎中,聚簇索引就是数据的存储 ⽅式(所有的⽤户记录都存储在了叶⼦节点),也就是所谓的索引即数据,数据即索引。

4.6 二级索引

问题:上面所说的索引都是围绕主键建立的,当搜索条件是其他列怎么办?

我们可以多建⼏棵B+树,不同的B+树中的数据采⽤不同的排序规则。⽐⽅说我们⽤c2列的⼤⼩作为数据⻚、⻚中记录的排序规则,再建⼀棵B+树,效果如下 图所示:

这个B+树与上面说的聚簇索引有几处不同:

  1. 页内使用记录c2列的大小顺序成一个单链表
  2. 各个存放⽤户记录的⻚也是根据⻚中记录的c2列列⼤⼩顺序排成⼀个双向链表。
  3. 存放⽬录项记录的⻚分为不同的层次,在同⼀层次中的⻚也是根据⻚中⽬录项记录的c2列⼤⼩顺序排成⼀个双向链表。
  4. B+树的叶子节点存储的并不是完整的用户记录,而只是c2列+主键这两个列的值。
  5. 目录像记录中也不再是主键+页号的搭配,而变成了c2列+页号的搭配。

假设我们现在想通过c2列为4的记录为例,查找过程如下:

  1. 根据最高层级的页44确定下一级的目录页42,因为(2<4<9)
  2. 根据页42可以找到真实数据所在的页
    在页42中可以快速定位到实际存储用户记录的页,但是由于c2列并没有唯一性约束,所以c2列值为4的记录可能分布在多个数据页中,又因为2<4<=4,所以可以确定用户记录存放到页34和页35
  3. 到真实页之后,再通过二分法槽找到具体的记录
  4. 最重要的一步二级索引的就是回表:根据主键再去聚簇索引中查找一遍完整的用户记录

问题:为什么用回表操作,不是将数据存储到叶子节点那?

如果把完整的⽤户记录放到叶⼦节点是可以不⽤回表,但是太占地 ⽅了呀~相当于每建⽴⼀棵B+树都需要把所有的⽤户记录再都拷⻉⼀遍,这就有点太浪费存储空间了。因为这种按照⾮主键列建⽴的B+树需要⼀次回表操作才可以 定位到完整的⽤户记录,所以这种B+树也被称为⼆级索引(英⽂名secondary index),或者辅助索引。由于我们使⽤的是c2列的⼤⼩作为B+树的排序规则,所以 我们也称这个B+树为为c2列建⽴的索引。

4.7 联合索引

我们也可以同时以多个列的大小作为排序规则,建立索引,比方说我们想让B+树按照c2和c3列的大小进行排序,这个包含两层含义:

  1. 先把各个记录和页按照c2列排序
  2. 在c2列相同的情况下,采用c3列进行排序

为c2列和c3列建立的索引的示意图如图:

如图所示,我们 需要注意以下几点:

  • 每个目录项记录都是由c2、c3和页号组成,各条记录先按照c2排序,再按照c3排序
  • 叶子节点是由c2、c3和主键c1组成。

其实,本质也是个二级索引,只是无论目录项记录还是数据页记录多了个列而已。

5.InnoDB索引细节

5.1 索引的生成过程 (比较重要)

  1. 每当为某个表创建一个B+树索引的时候,就会为这个索引创建一个根节点页面。最开始既没有目录页,也没有数据页。
  2. 随后向表中插入数据时,先把用户记录存储到根节点中。
  3. 继续向根节点插入数据后,此时根节点的数据会复制到一个新的页,比如页a,然后对这个页做页分裂,得到一个新页,比如页b。往后新插入的数据就会放到a或者b中,根节点就升级成了目录页

一个b+树索引的根节点自诞生之日后,就不会再移动。这样只要我们对某个表建⽴⼀个索引,那么它的根节点的⻚号便会被记录 到某个地⽅,然后凡是InnoDB存储引擎需要⽤到这个索引的时候,都会从那个固定的地⽅取出根节点的⻚号,从⽽来访问这个索引。

⼩贴⼠: 跟⼤家剧透⼀下,这个存储某个索引的根节点在哪个⻚⾯中的信息就是传说中的数据字典中的⼀项信息,关于更多数据字典的内容, 后边会详细唠叨,别着急哈。

5.2 非唯一二级索引

如果我们的数据是这样的:

如果二级索引中目录项记录的内容只是索引列+页号的话,那么图示:

这时候,如果我们想插入一行记录(9,1,‘c’),那么这个时候遇到一个很大的问题,这时候这条记录是插到页4还是页5那?

为了能够让插入的记录能找到自己所在的页,**我们需要保证B+树同一层节点的目录项记录除了页号以外这条记录是唯一的。**所以这时候我们需要再加入一个列:主键。

也就是我们把主键值也添加到⼆级索引内节点中的⽬录项记录了,这样就能保证B+树每⼀层节点中各条⽬录项记录除⻚号这个字段外是唯⼀的,所以我们为c2列建 ⽴⼆级索引后的示意图实际上应该是这样⼦的:

这时候,当我们想插入一条(9,1,‘c’)时,先比较c2列,如果c2列是一样的,再比较主键列,主键列肯定是不一样的,9>7,所以新纪录应该被插入到页5中。

6.总结

6.1 如果没有索引的查找会是很恐怖的,无论是在一个页还是多个页中查找,基本都是遍历的逻辑。

6.2 当我们想通过建立一个类似书籍目录一样的结构来索引数据页的时候,会存在很多问题,比如耗空间、多一种设计实现方式

6.3 最终,我们通过复用数据页的结构来索引数据页,这个叫做目录页,目录页的主要作用就是索引页

6.4 不同的索引具体实现方式

  • 聚簇索引,数据页主键+用户记录,目录页主键+页号
  • 二级唯一索引:数据页排序列+主键,目录页排序列+主键
  • 二级非唯一索引:数据页排序列+主键,目录页排序列+主键+页号 (非唯一的话,目录页多个主键列
  • 联合索引:数据页排序列1+排序列2+主键,目录页排序列1+排序列2+页号

6.5索引的生成方式

参照 5.1,结束peace

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

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

相关文章

SpringCloud Alibaba @SentinelResource 注解

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十五篇&#xff0c;即介绍 SpringCloud Alibaba 的 SentinelResource 注解。 二、简介 这个注解用于标…

Agenda家族再升级,跨平台定制化勒索引关注

Agenda勒索也被称为Qilin勒索&#xff0c;该家族的早期版本使用Go 语言编写的&#xff0c;增加了安全分析的难度。其早期版本是针对每位受害者定制的&#xff0c;使用受害者的机密信息&#xff08;例如泄露的帐户和唯一的公司 ID&#xff09;作为附加文件扩展名。针对早期版本A…

TS封装axios并约束请求参数以及响应的类型

封装一个简单的axios src/utils/axiosInstance.ts&#xff1a;其中定义了基本返回数据的类型 import axios, {AxiosInstance,AxiosResponse,AxiosError,AxiosRequestConfig, } from "axios"// 定义基本返回数据类型 export interface ApiResponse<T> {code: …

蓝桥杯-DS18B20温度传感器

一.管脚&芯片&寄存器 1.芯片 2.了解封装以及引脚的用法 3.相关寄存器 报警功能 二&#xff0c;如何使能DS18B20芯片 1.初始化芯片&比赛提供的驱动代码 比赛提供的底层驱动代码 /* # 单总线代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考…

基于R语言lavaan结构方程模型(SEM)实践技术应用

原文链接&#xff1a;基于R语言lavaan结构方程模型&#xff08;SEM&#xff09;实践技术应用https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247596681&idx4&sn08753dd4d3e7bc492d750c0f06bba1b2&chksmfa823b6ecdf5b278ca0b94213391b5a222d1776743609cd…

跨服务器迁移nextcloud数据

背景 阿里云最近做活动,99一年的2U2G的服务器,比我原来的1U1G的服务器不知道高到哪里去了,于是决定迁移服务器数据到另一台主机上。原先的计划是直接做一个自定义镜像,然后复制到另一台主机就行,结果发现旧主机是aliyunOS,新主机不想踩这个坑了,决定换成乌班图,因此决定重新搭…

Mac系统Unity团结引擎打包OpenHomeny项目配置

1、团结引擎下载&#xff1a;直接百度下载即可 2、mac版本的DevEco4.0编辑器下载&#xff1a; widthdevice-width,initial-scale1.0https://docs.openharmony.cn/pages/v4.0/zh-cn/release-notes/OpenHarmony-v4.0-release.md/#%E9%85%8D%E5%A5%97%E5%85%B3%E7%B3%BB3、打开D…

什么是多路复用器滤波器

本章将更深入地介绍多路复用器滤波器&#xff0c;以及它们如何用于各种应用中。您将了解到多路复用器如何帮助设计人员创造出更复杂的无线产品。 了解多路复用器 多路复用器是一组射频(RF)滤波器&#xff0c;它们组合在一起&#xff0c;但不会彼此加载&#xff0c;可以在输出之…

智能化办公时代来临:AI助你解放双手

文章目录 一、AI在办公领域的广泛应用二、AI助力办公效率提升1.自动化流程减少繁琐任务2.智能分析辅助决策制定3.个性化服务提升用户体验 三、AI提升办公效率的未来趋势1.更加智能化的办公场景2.更高效的团队协作3.更全面的数据安全保护 四、应对AI带来的挑战《AI高效工作一本通…

RocketMQ笔记(七)SpringBoot整合RocketMQ发送事务消息

目录 一、简介1.1、流程图1.2、事务消息流程介绍 二、Maven依赖三、生产者3.1、application配置3.2、员工表3.3、实体3.4、持久层3.5、监听器 四、测试4.1、普通消息4.2、事务消息4.2.1、消费者4.2.2、正常提交4.2.3、异常提交 五、其他5.1、接口说明5.2、checkLocalTransactio…

「Qt Widget中文示例指南」如何实现一个分组框

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 Group Box&#xff…

python中for与while的区别是什么

Python中for循环和while循环本质上是没有区别的&#xff0c;但是在实际应用上&#xff0c;针对性不太一样。 for主要应用在遍历中&#xff0c;比如&#xff1a; example1&#xff1a; for i in range(10):print(i) 打印结果为&#xff1a; 0 1 2 3 4 5 6 7 8 9 注&#xff1a;…

springboot系列-api接口请求实现

springboot HTTP请求接口实现 基于springboot 2.6.6 代码地址&#xff1a;github仓库地址 更多系列教程请关注公众号’coderlike’ 如果觉得有帮助希望能关注下公众号 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-b…

IP网络对讲广播系统审计

前言 这个系统是前两年在一个内网遇到的&#xff0c;当时顺手试了一个admin登陆之后再没有然后了&#xff0c;最近发现有大佬分享关于这个系统的漏洞&#xff0c;于是就把自己当初看的几个漏洞分享一下&#xff0c;系统比较简单&#xff0c;漏洞点很多&#xff0c;不要做坏事哦…

PaddleVideo:PP-TSM 视频分类

本文记录&#xff1a;使用Paddle框架训练TSM&#xff08;Temporal Shift Module&#xff09; 前提条件&#xff1a;已经安装Paddle和PadleVideo&#xff0c;具体可参考前一篇文章。 1-数据准备&#xff1a; 以UCF101为例&#xff1a;内含13320 个短视频&#xff0c;视频类别&…

uniapp引入微信小程序版本VantUI,使用VantUI的自定义tabbar,并解决自定义tabbar出现闪烁的情况

1.uniapp引入微信小程序版本VantUI 去vant官网下载源码&#xff0c;源码放在github&#xff0c;自行去下载下来 https://vant-contrib.gitee.io/vant-weapp/#/home 在pages.json的globalStyle里面注册组件 "globalStyle": {"navigationBarTextStyle": &qu…

zookeeper源码(12)命令行客户端

zkCli.sh脚本 这个命令行脚本在bin目录下: ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)"# 加载zkEnv.sh脚本 if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; …

程序员开展副业的有效方法

目录 1 项目咨询1.1 建立个人品牌和专业形象1.2 寻找潜在客户1.3 提供定制化的技术咨询方案 2 软件开发2.1 加入平台寻找项目2.2 展示个人作品2.3 与客户保持良好沟通 3 传授编程知识3.1 选择适合的分享平台3.2 创作有价值的内容3.3 与观众保持互动 4 教育培训4.1 利用在线教育…

RuoYi-Vue若依框架-在框架内用颜色选择器,页面显示色块

在用若依框架进行二次开发的时候写到自己的一个模块&#xff0c;其中涉及到颜色&#xff0c;我就想着是手动输入还是采用颜色选择器呢&#xff0c;考虑到后续涉及到另一个字段编码于时就采用了颜色选择器&#xff0c;选择完的颜色显示的是十六进制的颜色选择器&#xff0c;这时…

零基础学鸿蒙开发可以吗?看完这份鸿蒙入门学习资料就够了!

一、面向人群 1、在校学生、应届毕业生 2、转行人员&#xff0c;希望赶上时代风口&#xff0c;成功求职、转行 3、IT相关工作者&#xff0c;想快速提升技能&#xff0c;升职加薪的朋友 ps&#xff1a;文末可以申请免费试学 二、学习路线 HarmonyOS基础技能HarmonyOS就业…