数据库索引底层数据结构之B+树MySQL中的页索引分类【纯理论知识,干货分享,面试必备】

news2024/11/18 7:43:41

目录

1、索引简介

1.1 什么是索引

1.2 使用索引的原因

2、索引中数据结构的设计 —— B+树

2.1 哈希

2.2 二叉搜索树

2.3 B树

 2.4 最终选择之——B+树

2.4.1 B树与B+树的对比(面向索引)【面试题】

3、MySQL中的页

3.1 页的使用原因

 3.2 页的结构

3.2.1 页文件头和页文件尾

 3.2.2 页主体

3.2.3 页目录

3.2.4 数据页头

4、B+树在MySQL索引上的应用

 4.1 三层树高的B+树的数据存储量(理论上)

5、索引的分类

5.1 主键索引

5.2 普通索引

5.3 唯一索引

5.4 全文索引(了解)

5.5 聚集索引

5.6 二级索引/非聚集索引

5.7 索引覆盖


1、索引简介

1.1 什么是索引

MySQL中的索引是一种数据结构,它可以高效快速的查询、更新相应数据,大大的提高开发效率。索引通过⼀定的规则排列数据表中的记录,使得对表的查询可以通过对索引的搜索来加快速度。

我们可以将索引理解为书中的目录,可以根据目录快速的锁定到我们要阅读的页数。

1.2 使用索引的原因

 数据库之所以叫数据库,那么MySQL的设计必然需要满足两个需求:

  1. 安全
  2. 效率

 显而易见,使用索引的原因只有一个:提高查询效率。


2、索引中数据结构的设计 —— B+树

 索引的出现就是为了提高查找效率,那它底层到底使用的是哪种高效的数据结构呢?让我们继续探索。

2.1 哈希

说到高效查找 ,相比大家第一个想到的就是哈希表,因为它的时间复杂度可以达到O(1)级别,可谓是极其高效,正因为其高效的查找性能也使哈希成为最重要的数据结构,没有之一。但是由于哈希只能一个数据一个数据的查找,不具备范围查找的功能,故索引并没有使用哈希作为它的底层结构。

2.2 二叉搜索树

二叉搜索树中序遍历得到的是一个有序序列,这使得其具备范围查找的功能。

但是,二叉搜索树在极端情况下可能为退化成一棵单分支树,时间复杂度也会退化为O(N)。

并且在数据过多的情况下,无法控住树高,由于数据库上的数据是在磁盘上保存的,而每访问一个节点都会发生一次磁盘IO,磁盘的访问速度是很慢的,所以当搜索二叉树出现单分支形态时,将严重拉低数据库性能。

由此得出:磁盘IO是制约数据库性能的主要因素。【拓展】

故索引也没有使用二叉搜索树作为它的底层结构。

2.3 B树

B树仍具备中序遍历序列有序的特点,在B树中,每个节点可以有多个子节点(可以超过2个)。对于N阶B树(度/阶),一般来说其子节点个数不可>=N个,当插入的节点为第N个孩子时,将开辟另一条分支。

由于其可以有多个孩子节点,故其可以有效的控制树高,时间复杂度也稳定为O(logN),这也意味着将会降低磁盘IO开销,提升数据库性能。

 2.4 最终选择之——B+树

上面提到B树不仅可以满足数据库范围查询的要求,而且可以降低IO开销,但是开发数据库的大佬并没有将B树作为索引的底层结构,因为他们发现了更加高效更加适合的数据结构——B+树。

B+树在满足B树可控树高、高效O(logN)查询、... 的优秀性能下,进一步做了优化。

2.4.1 B树与B+树的对比(面向索引)【面试题】

 1.B+树的叶子节点间使用单链表连接,可以通过一个叶子节点找到它相邻的兄弟节点

  • 且MySQL在组织叶子节点时,使用的是双向循环链表

2.B+树中,所有节点的值都包含在了叶子节点中

  • 在MySQL中,非叶子节点只保留了对子结点的引用,并不保存真实数据,所有的真实数据都保存在叶子节点中。因此,对于B+树而言,在相同树高的情况下,查询任意元素的时间复杂度都是相同的,性能均衡。


3、MySQL中的页

3.1 页的使用原因

MySQL中,使用innodb存储引擎后生成的表空间文件后缀都为.ibd。而在.ibd文件中,最重要的结构体就是页(Page),页是内存与磁盘交互的最小单元,默认大小为16KB。

show variables like 'innodb_page_size'; #查看页大小,单位字节

每次内存与磁盘的交互至少读取一页,所以在磁盘中每个页内部的地址都是连续的。之所以这样做有以下两点原因:

  1. 时间局部性:如果⼀个信息项正在被访问,那么在近期它很可能还会被再 次访问
  2. 空间局部性:将来要使用的数据大概率与当前访问的数据在空间上是临近的

所以根据局部性原理, 以⼀次从磁盘中读取一页的数据放入内存中,当下次查询的数据还在这 个页中时就可以从内存中直接读取,从而减少磁盘I/O,提高性能。

注意:即使一个页中没有数据,也会使用16KB的存储空间。同时与索引的B+树中的节点对应,也就是说,索引树中的每一个节点,就对应一个页。

 3.2 页的结构

在MySQL中有多种不同类型的页,最常用的就是用来存储数据和索引的"索引页",也叫做"数据 页",但不论哪种类型的页都会包含页头(File Header)和页尾(File Trailer),页的主体信息使用数 据"行"进行填充。

每创建一个表,生成一个保存数据的文件,即.ibd文件,也称为独立表空间文件。

上文说到,一个.ibd文件中,最重要的结构就是页,接下来,让我们一起探讨页的组成结构。

3.2.1 页文件头和页文件尾

页文件头和页文件尾中,包含了当前文件的主要信息:

这⾥我们只关注,上一页页号和下一页页号,通过这两个属性可以把页与页之间链接起来(通过页号和页大小,可以计算出上一页和下一页在磁盘上的偏移量),形成⼀个双向循环链表。 

 3.2.2 页主体

页主体部分是保存真实数据的主要区域,每当创建⼀个新页,都会自动分配两个行。

  • ⼀个是页内最小行 Infimun
  • ⼀个是页内最大行 Supremun

这两个行并不存储任何真实信息,而是作为数据行链表的头和尾。

并且,每一个数据行有一个记录下一行的地址偏移量的区域:next_record,故此,页内所有数据行组成了⼀个单向链表。

 当向⼀个新页插入数据时,将 Infimun 连接第一个数据行,最后一行真实数据行连接 Supremun ,这样数据行就构建成了一个单向链表,更多的行数据插入后,会按照主键从小到大的顺序进行链接,自动排序。

也就是说,所有数据行间形成单向链表:最小化Infimun始终为链表的头,最大行Supremun始终为链表的尾。

3.2.3 页目录

上文提到,页中的每个数据行间形成了一个单向链表,但是单链表的时间复杂度为O(N),无法满足高效查询,为了提高查询效率,InnoDB采用二分查找,以加入页目录的结构来解决查询效率问题。

页目录实现形式:将页内包括头行(最小行Infimun )、尾行(最大行Supremun )在内的所有行进行分组。具体实现如下:

  1. 最小行单独为一组
  2. 其他每个组最多8条数据,超过8条数据时会开辟出一个新的分组
  3. 最大行永远在最后一个分组中
  4. 每创建一个分组,页目录就会创建一个槽,即槽的数量与组的数量是一致的,且每个分组与槽都一一对应
  5. 槽中记录了其对应分组的最后一条记录的地址,同时记录这条记录的主键值

经过分组和槽的创建,查找操作时就能够以二分查找性能快速定位到要目标数据,大大提高了查找效率。

例如,我们要查找某一条存在的记录:

  1. 第一步,通过索引树找到该记录所在页。
  2. 第二步,通过槽找到该记录所在分组。(每个槽都记录了该分组最后一条记录的主键值和地址,因为页中所有数据行通过主键有序排列,通过比较槽中的主键值,可快速定位到目标记录所在分组)
  3. 第三点,遍历该分组的单链表(最多只有8条数据),查找成功。

3.2.4 数据页头

数据页头记录了当前页保存数据相关的信息。


4、B+树在MySQL索引上的应用

在索引的B+树的叶子节点和非叶子节点中:

  • 非叶子节点和叶子节点均为页,称为索引页/数据页。
  • 非叶子节点只记录索引信息。在索引页中保存的是主键值和叶子节点的引用。
  • 叶子节点存储的是真实的数据。数据页保存的是具体的数据。
  • 最后一层的叶子节点中,页与页之间通过页号建立起关联关系(页号信息在页头中存储),最终形成了一个双向循环链表。

例如查找id为5的记录:

  1. 判断B+树根节点的索引记录,5<7,进入左孩子节点,找到索引页2
  2. 判断索引页2的索引记录,找到与5相等的记录,命中
  3. 进入数据页3

 故以上过程共经过3次IO过程:加载索引页1-->加载索引页2-->加载数据页3

 4.1 三层树高的B+树的数据存储量(理论上)

上文已说明,一个页的大小为16KB。

  • 第一层:根节点(索引页)

一个索引页存储的是主键值和子节点的引用。假设主键值占8Byte,地址占6Byte,故一条索引记录的大小为8+6=14Byte。故一个索引页可保存16*1024/14=1170条索引记录。故B+树的根节点可保存1170条索引记录,即可指向1170个子节点。

  • 第二层:非叶子节点(索引页)

同样,第二层的每个索引页可指向1170个子节点(数据页)。

  • 第三层:叶子结点(数据页)

算上页中页头、页尾、页主体....等结构空间的占比,假设一个页中最多可以保存16条记录。故,三层树高的B+树一共可存储 1170*1170*16=21,902,400条记录。

综上,三层树高的B+树一共可存储 1170*1170*16=21,902,400条记录,而在存储这么多数据的情况下,只需要经过3次IO就可以查询到目标数据(加载索引页1-->加载索引页2-->加载数据页3),可以说是极其高效。

而且,如果将索引页1、索引页2加载到内存中进行缓存,那么只需一次IO就可以查询成功,效率极高。


5、索引的分类

5.1 主键索引

  • 主键索引也叫做聚集索引、聚簇索引
  • 当在表中定义一个主键(PRIMARY KEY)时,将自动创建主键索引,索引中的值就是主键列的值
  • 故推荐为每一个表定义一个主键。

5.2 普通索引

  • 最基本、最常用的索引类型,没有唯一性限制。
  • 为了提升查询效率,工作中通常为查询频繁的列创建索引。
  • 可以为多个列创建组合索引,称为复合索引或组合索引。
  • 创建索引的前提是:列的值重复性不高。例如:gender(性别)列,该列值要么为1(男),要么为2(女),就没有必要创建索引。
  • 创建索引之后都会生成一棵索引树,创建多少索引就会生成多少棵索引树。

注意:

创建索引所生成的索引树也是会占用磁盘空间的。

所以,在创建索引时,一定要慎重考虑需不需要创建。

索引树越多,对增、删、改的效率影响越大,将会拉低数据库性能。

所以没有必要创建冗余索引(如上文的gender列)。

5.3 唯一索引

  • 当表中定义有UNIQUE唯一键时,将自动创建唯一索引。

  • 与普通索引类型,但唯一索引的列不允许有重复值。 

5.4 全文索引(了解)

  • 基于文本列(CHAR、VARCHAR或TEXT列)上创建,以加快对这些列中包含的数据查询和DML操作
  • 用于全文搜索,仅MyISAM和InnoDB引擎支持。

5.5 聚集索引

  • 即上文提到的主键索引。

  • 如果表中没有定义主键(PRIMARY KEY),innoDB将使用第一个UNIQUE和NOT NULL的列作为聚集索引(聚集索引可以标识数据行的唯一性)

  • 如果表中没有主键或者唯一非空列作为合适的聚集索引时,innoDB会自动新增ROW_ID列,ROW_ID单调递增,并使用ROW_ID作为索引。(ROW_ID为数据行中的隐藏列之一,我们查询不到,也使用不了)

5.6 二级索引/非聚集索引

  • 聚集索引以外的索引称为非聚集索引或二级索引
  • 二级索引中的每条记录都包含了该行的主键列,以及二级索引指定的列。
  • InnoDB通过二级索引的索引树查询到主键值,再利用主键值通过主键索引树查询相关记录,这个过程称为回表查询。
  • 创建复合索引时,如果name列在sn列的前面,则使用where条件时,也要先使用name再使用sn,也不能只使用sn(使用AND时,两列顺序不作要求)。如果只使用sn列,那么索引失效(不走索引)。如果非要使用sn列来条件查询,可以为sn单独创建一个索引。可以这样理解:查字典时,必须先找声母再找韵母,而不能先找韵母再找声母。

5.7 索引覆盖

  • 当一个select语句使用了普通索引且查询列表中的列刚好是创建普通索引时的所有或部分列,这时就可以直接返回数据,而不用回表查询,这样的现象称为索引覆盖
  • 通过索引查找的列,包含在索引中,不需要回表查询,这种情况就是索引覆盖。

END

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

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

相关文章

解锁定位服务:Flutter应用中的高德地图定位

前言 在现代移动应用开发中&#xff0c;定位服务已成为一项基本功能&#xff0c;它使得应用能够获取用户的地理位置信息&#xff0c;为用户提供更加个性化的服务。 Flutter 作为跨平台的移动应用开发框架&#xff0c;支持集成多种服务&#xff0c;包括定位服务。 本文将介绍如…

HR8870:可PWM控制,4.5A直流有刷电机驱动数据手册

HR8870芯片描述 HR8870是一款直流有刷电机驱动器&#xff0c;适用于打印机、电器、工业设备以及其他小型机器。两个逻辑输入控制H桥驱动器&#xff0c;该驱动器由四个N-MOS组成&#xff0c;能够以高达4.5A的峰值电流双向控制电机。利用电流衰减模式&#xff0c;可通过对输入进行…

故障码格式解析

中&#xff0c;诊断故障码&#xff08;DTC, Diagnostic Trouble Code&#xff09;是由一个字母前缀和三个后续字符组成的。这些字母前缀根据故障所属的系统类别来区分&#xff0c;具体如下&#xff1a; B0 -- B3&#xff1a;表示车身系统&#xff08;Body&#xff09;的故障码…

Linux CTF逆向入门

1.ELF格式 我们先来看看 ELF 文件头&#xff0c;如果想详细了解&#xff0c;可以查看ELF的man page文档。 关于ELF更详细的说明&#xff1a; e_shoff&#xff1a;节头表的文件偏移量&#xff08;字节&#xff09;。如果文件没有节头表&#xff0c;则此成员值为零。 sh_offset&…

Qt 菜单、工具栏 的基本使用

效果 代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include<QToolBar> #include<QDebug> #include<QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupU…

【JAVA入门】Day45 - 压缩流 / 解压缩流

【JAVA入门】Day45 - 压缩流 / 解压缩流 文章目录 【JAVA入门】Day45 - 压缩流 / 解压缩流一、解压缩流二、压缩流 在文件传输过程中&#xff0c;文件体积比较大&#xff0c;传输较慢&#xff0c;因此我们发明了一种方法&#xff0c;把文件里的数据压缩到一种压缩文件中&#x…

【LLMs对抗性提示:提示泄漏、非法行为、DAN、Waluigi效应、 游戏模拟器、防御策略————】

对抗性提示 目录 对抗性提示 提示注入 提示泄漏 非法行为 DAN Waluigi效应 GPT-4模拟器 游戏模拟器 防御策略 在指令中添加防御 参数化提示组件 引用和其他格式 对抗提示检测器 模型类型 参考文献 Adversarial prompting是提示工程中的一个重要主题&#xff0c…

每日OJ_牛客_NC313 两个数组的交集

目录 牛客_NC313 两个数组的交集 解析代码 牛客_NC313 两个数组的交集 两个数组的交集_牛客题霸_牛客网 class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param nums1 int整型vector * pa…

统计/nginx/access.log中每个ip的访问次数,按高到低排列

/nginx/access.log具体内容长这样&#xff1a; 第一个元素就是ip。 awk {print $1} /nginx/access.log | sort | uniq -c | sort -r首先&#xff0c;awk {print $1} /nginx/access.log 从 /nginx/access.log文件的每行中提取出第一个字段。然后&#xff0c;sort 对提取出的第…

【有哪些坑】Apollo配置中心FAQ常见问题列表

使用某个框架之前&#xff0c;得先看看前辈们踩过的坑。 他人的间接经验 -> 自己的直接经验 前车之鉴&#xff0c;后事之师。比喻前人失败了&#xff0c;后人应该从中吸取教训&#xff0c;避免再犯同样的错误。 常见问题回答 1. Apollo是什么&#xff1f; Apollo&#xff…

关于STM32项目面试题01:电源

博客的风格是&#xff1a;答案一定不能在问题的后面&#xff0c;要自己想、自己背&#xff1b;回答都是最精简、最精简、最精简&#xff0c;可能就几个字&#xff0c;你要自己自信的展开。 面试官01&#xff1a;说说你知道的开关电源的拓扑结构&#xff1f; 面试官02&#xff1…

Nacos下载和启动

Nacos是什么&#xff1f; 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台 下载 https://github.com/alibaba/nacos/releases/tag/2.1.1启动 将下载好的Nacos解压缩&#xff0c;然后到bin目录下打开cmd 输入指令&#xff1a;startup.cmd -m standalone 出…

Apache DolphinScheduler 跨工作流复杂依赖功能详解

大家好&#xff0c;我叫高楚枫&#xff0c;来自阿里云 EMR 团队的开发工程师&#xff0c;同时也是 Apache DolphinScheduler 的 PMC 成员之一。 今天非常高兴能在这里和大家分享关于跨工作流复杂依赖的功能详解。 引言 在现代的数据处理和调度过程中&#xff0c;工作流的依赖…

STL_string 常用的用法

string里常用的函数与讲解使用 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ &#xff08;点击进入c关于string 的各个函数的全面讲解使用/英文版&#xff09; Iterators&#xff08;迭代器&#xff09;: begin与end&#xff1a; …

门磁模块详解(防盗感应开关 STM32)

目录 一、介绍 二、程序设计 main.c文件 gate_guard.h文件 gate_guard.c文件 三、实验效果 四、资料获取 项目分享 一、介绍 MC-38常闭式门磁开关是作为IO开关输入数字信号的&#xff0c;原理是合在一起信号是导通的 , 配合有线主机使用 不能单独使用。适用于非铁质&a…

RK3588镜像打包制作,替换文件系统

1.在开发板上安装async apt-get async 2.在另一台linux机器上执行命令拷贝文件系统 注意&#xff1a; 这里使用root权限或者账户 mkdir rootfs rsync -avx root192.168.1.3:/ rootfs 3.制作空镜像文件 先去开发板上验证自己的系统使用了多少空间&#xff0c;然后输入命令制…

grafana升级指南

已有grafana在使用&#xff0c;需要升级新版本的grafana&#xff0c;操作如下&#xff1a; 1.先把之前的grafana文件夹整个备份 2.在grafana官网下载OSS的zip版本&#xff0c;不要msi版本 3.在原来的grafana文件夹里&#xff0c;把新版本的文件夹都复制进来&#xff0c;但是…

CVE-2024-21096:MySQLDump提权漏洞分析

CVE-2024-21096是一个中等严重性的漏洞&#xff0c;它影响Oracle MySQL Server产品中的mysqldump组件。成功利用此漏洞的未认证攻击者可能对MySQL Server的数据进行未授权的更新、插入或删除操作&#xff0c;还可以读取MySQL Server可访问数据的一部分&#xff0c;并可能导致My…

代码随想录算法训练营第五十九天 | dijkstra(堆优化版)精讲

目录 dijkstra&#xff08;堆优化版&#xff09;精讲 思路 堆优化细节 方法一&#xff1a; 最小堆优化 dijkstra&#xff08;堆优化版&#xff09;精讲 题目链接&#xff1a;卡码网&#xff1a;47. 参加科学大会 文章讲解&#xff1a;代码随想录 小明是一位科学家&#x…

MySQL 事务的 ACID 特性与应用

MySQL事务的ACID特性与应用 数据库事务 是保障数据一致性和完整性的关键机制。事务不仅是并发控制的核心&#xff0c;更是数据恢复的基本单位。本文将带你深入了解MySQL中的事务概念、ACID特性以及如何在实际应用中正确处理事务。 1. 什么是事务&#xff1f; 事务&#xff0…