MySQL —— Innodb 索引数据结构

文章目录

  • 不用平衡二叉树或红黑树作为索引
  • B树适合作为索引
  • 比B树更适合作为索引的结构——B+树
  • 总结

MySQL 使用 B+树索引数据结构(因为默认使用 innodb 存储引擎)

  • B树:有序数组 + 平衡多叉树;
  • B+树:有序数组链表 + 平衡多叉树;

不用平衡二叉树或红黑树作为索引

普通二叉树:

  • 二叉树的查找的时间复杂度是 O ( l o g 2 N ) O(log_2N) O(log2N),其查找效率与深度有关,而普通的二叉树可能由于内部节点排列问题退化成链表。例如,按顺序插入一组递增的数值,可能导致树变成一条链表,在这种情况下,树的深度变为 N,查找的时间复杂度退化为 O ( N ) O(N) O(N),这与链表的查找效率相同。这样查找效率就会很低。
    1
     \
      2
       \
        3
         \
          4
    

平衡二叉树

  • 平衡二叉树是更好的选择,通过自平衡机制保持平衡,即通过旋转调整结构保持最小的深度,所有节点的左右子树高度差不能超过 1,确保了查找、插入和删除操作的时间复杂度保持在 O ( l o g 2 N ) O(log_2N) O(log2N)

为什么平衡二叉树不适合作为索引呢?

  • 索引通常存储在磁盘上的索引文件中。在数据表很大的情况下,索引也会很大,因此无法一次性将全部索引加载到内存当中。数据库系统通常以页(如 4KB、8KB 或 16KB)为单位从磁盘中读取一个磁盘页的数据量到内存中,为了找到指定索引的磁盘页,可能会导致多次磁盘 I/O 操作才能完成。而这个磁盘的读取的速度较内存中的读取速度而言是差了好几个级别,磁盘的随机访问时间可能是内存访问时间的数百倍至数千倍,这意味着每次从磁盘读取数据时,都会消耗较多的时间。

    • 每个节点只有两个子节点,这导致树的高度可能较大。当树的深度增加时,查找时需要的磁盘 I/O 操作也会相应增加。例如,在最坏情况下,查找可能需要访问树的每一层,而每一层都需要从磁盘读取数据。

  • 平衡二叉树在逻辑结构上相近的节点在树的结构中是相邻的,其物理实现是使用数组来存储节点。虽然在逻辑上相邻的节点可能在物理数组中相距较远,因此,每次读取的磁盘页的数据中有许多是用不上的。因此,查找过程中要进行许多次的磁盘读取操作。

    • 平衡二叉树各节点之间在逻辑上存在一定的关系,但在物理实现却是随机的,比如父子关系或兄弟关系的两个节点子逻辑上相邻之类的,但在物理实现上却是分散的。对于使用磁盘预读(局部性原理),每次从磁盘读取一页,读取的是物理结构上连续的一段数据,这样就会读取许多无用的信息(接下来很大机会不会用到);

    • 其次就是二叉树每个节点只能存储一个关键字及其相关信息,不能充分利用磁盘预读功能;

  • 而适合作为索引的结构应该是尽可能少的执行磁盘IO操作,因为执行磁盘IO操作非常的耗时。因此,平衡二叉树并不适合作为索引结构。

总结:

  • 使用红黑树(平衡二叉树)结构的话,每次磁盘预读中的很多数据是用不上的数据。因此,它没能利用好磁盘预读的提供的数据。
  • 然后又由于深度大(较B树而言),所以进行的磁盘IO操作更多。

B树适合作为索引

平衡二叉树没能充分利用磁盘预读功能,而B树是为了充分利用磁盘预读功能来而创建的一种数据结构,也就是说B树就是为了作为索引才被发明出来的的。

  • B 树的设计旨在最大限度地减少磁盘访问次数,特别是在处理大量数据时。由于磁盘访问速度远低于内存访问速度,减少 I/O 操作对于提高整体性能至关重要;
  • B 树结构允许每个节点包含多个关键字和子节点,这意味着在一次磁盘读取中可以获取更多的数据。这样,B树能够高效地利用磁盘的读取特性,特别是在顺序访问时;

B树的结构特点

  • 多叉结构:与平衡二叉树不同,B 树的每个节点可以有多个子节点,这使得树的高度显著降低,从而减少了查找和更新时所需的 I/O 操作;
  • 节点内存储多个关键字:B 树的每个节点可以存储多个关键字,这样在每次读取磁盘页时,可以同时获取多个数据项,充分利用了磁盘的预读能力;

局部性原理与磁盘预读:

  • 由于存储介质的特性,磁盘的存取速度远低于内存,传统机械硬盘的访问时间受机械运动的影响,磁盘的存取速度往往是主存的几百分分之一,这使得磁盘 I/O 成为性能瓶颈,尤其是在需要频繁读取数据时。因此为了提高效率,要尽量减少磁盘 I/O。为了达到这个目的,磁盘读取往往不是严格按需读取,而是每次都会预读,读取一段连续的数据(称为一页),即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的目的是利用磁盘的顺序读取特性,提高读取效率。这样做的理论依据是计算机科学中著名的局部性原理;

  • 局部性原理:局部性原理是指在程序运行过程中,访问的数据往往集中在某些特定区域。当一个数据项被访问时,其附近的数据项很可能也会被访问。这种现象在许多程序中普遍存在。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率;

    在操作系统中,磁盘 I/O 的读取和写入通常以页(page)为单位进行。页是固定大小的数据块,一页的数据量有多大跟操作系统有关,常见的大小有 4KB、8KB 或 16KB。也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助;

  • 磁盘预读是具体实现,其理论依据是局部性原理。

B树适合作为索引

B 树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小,充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点。也正因每个节点存储着非常多个关键字,树的深度就会非常的小。进而要执行的磁盘读取操作次数就会非常少,更多的是在内存中对读取进来的数据进行查找。

  • 每个节点可以存储多个关键字,可以充分利用了磁盘预读的功能;
  • B 树的深度会非常的小,减小磁盘 I/O 操作。

B树的查询,主要发生在内存中,而平衡二叉树的查询,则是发生在磁盘读取中。因此,虽然B树查询查询的次数不比平衡二叉树的次数少,但是相比起磁盘IO速度,内存中比较的耗时就可以忽略不计了。因此,B树更适合作为索引。

比B树更适合作为索引的结构——B+树

B+树中的B不是代表二叉(binary),而是代表平衡(balance),因为B+树是从最早的平衡二叉树演化而来,但是 B+树不是一个二叉树。

MySQL 中也是使用B+树作为索引。它是B树的变种,因此是基于B树来改进的。

B+树的关键字全部存放在叶子节点中,非叶子节点用来做索引,而叶子节点中有一个指针指向一下个叶子节点。做这个优化的目的是为了提高区间访问(遍历)的性能。

  • B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持 range-query 非常方便,而B树不支持。这是数据库选用B+树的最主要原因。

    比如说:

    我们要查找关键字范围在3到7的关键字,在找到第一个符合条件的数字3后,访问完第一个关键字所在的块后,得遍历这个B树,获取下一个块,直到遇到一个不符合条件的关键字。遍历的过程是比较复杂的。(即B树遍历完一块后,需要查询B树得到下一块,然后再遍历)
    
    相比之下,B+树的基于范围的查询简洁很多。由于叶子节点有指向下一个叶子节点的指针,因此从块1到块2的访问,通过块1指向块2的指针即可,从块2到块3也是通过一个指针即可。(B+树遍历完一块后,可以通过指针指向下一块继续遍历)
    
  • B+树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。

b+树的查找过程:

  • 如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
    在这里插入图片描述

    一颗b+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

总结:用B+树的主要原因是B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。正是为了解决这个问题,B+树应运而生。

总结

B树:

  • B树就是一个节点可以拥有多于2个子节点的多叉查找树。

  • 叶子节点和非叶子节点都储存关键字和真实数据项,每个节点不仅用于索引,还包含实际的数据。因为非叶子节点要保存数据项,所以一个节点(块)能保存的索引少,相对于B+树访问磁盘IO时就多;

  • B树是有序数组+平衡多叉树;

B+树:

  • B+树是基于B树来改进的。

  • 非叶子节点只存储关键字,用于索引搜索路径,而不存储实际的数据项,所有真实数据只存储在叶子节点中。因为非叶子节点不保存数据项,所以一个节点(块)能保存的索引多,相对于B树访问磁盘IO时就少,尽管在内存遍历的次数变多,但内存访问比磁盘快很多,相对于访问磁盘,访问内存的时间几乎可以不计。

  • B+树:有序数组链表+平衡多叉树;

    树的所有叶子节点构成一个有序链表,可以按照主键排序的次序遍历全部记录。B+Tree 所有真实数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都有指向相邻叶子节点的指针,便于区间查找和遍历。

B树与B+树对比:

  • B树结构在所有的节点里存储索引信息和数据项;B+树结构没有在所有的节点里存储记录数据项,而是只在最下层的叶子节点存储,上层的所有非叶子节点只存放索引信息。
  • B树是有序数组+平衡多叉树;B+树是有序数组链表+平衡多叉树。B+树使用双向链表串连所有叶子节点,区间查询效率更高,因为所有数据都在B+树的叶子节点,但是B树则需要通过中序遍历才能完成查询范围的查找。
  • B+树每次都必须查询到叶子节点才能找到数据,而B树查询的数据可能不在叶子节点,也可能在,这样就会造成查询的效率的不稳定。
  • B+树查询效率更高,因为B+树矮更胖,高度小,查询产生的I/O最少。因为非叶子节点不保存数据项,所以一个节点(块)能保存的关键字(索引)更多,所以一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

B+ 树的优点在于:

  • IO次数更少(相对于其他数据结构):由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。
    • 数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点需要一次I/O就可以完全载入。
  • 遍历更加方便(相对于B树结构):B+树的叶子结点都是相链的,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。
    • 这样做是为了提高区间效率,例如查询key为从18到49的所有数据记录,当找到18后,只要顺着节点和指针顺序遍历就可以以此向访问到所有数据节点,极大提高了区间查询效率。

B树优点:

  • 但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。下面是B 树和B+树的区别图:
    在这里插入图片描述

为什么MySQL选择B+树做索引

1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。

2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、B+树更便于遍历:由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

4、B+树更适合基于范围的查询:B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。

参考1

参考2

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

shell中执行hive指令以及hive中执行shell和hdfs指令语法

0. 简介 主要介绍了三种环境命令执行语法&#xff1a; shell中执行hive指令hive中执行shell指令hive中执行hdfs指令 1. shell中执行hive指令 语法&#xff1a;hive [-hiveconf xy]* [<-i filename>]* [<-f filename> | <-e query-string>] [-S] 说明&…

MySQL系列之如何在Linux只安装客户端

导览 前言Q&#xff1a;如何安装一个Linux环境下的MySQL客户端一、准备文件1. 确认Server版本2. 选择Client安装文件 二、下载并安装1. 下载1.1 寻找文件1.2 文件说明 2. 安装2.1 上传至Linux服务器2.2 执行安装 三、连接验证1. 确认远程授权2. 建立远程连接 结语精彩回放 前言…

C++20 概念与约束(3)—— 约束的进阶用法

1、再谈约束主句与从句 上一篇文章中提到过约束可以无限嵌套。末尾也提到不考虑嵌套约束的情况下&#xff0c;模板因为 SFINAE 规则的存在&#xff0c;其中 requires 子句只要存在返回值&#xff0c;只有可能是 true 这一种结果。在非模板中&#xff0c;如果 requires 子句中的…

进程启动时,main 函数是如何被找到的?

Linux中一个进程是如何被启动起来的&#xff1f; 一、进程是怎么启动的&#xff1f;二、进程内存空间分段三、进程的入口函数四、总结 一、进程是怎么启动的&#xff1f; 当一个程序被执行时&#xff0c;怎么看出进程的运行呢&#xff1f;一个进程是怎么启动的&#xff1f;为什…

关于 el-table 的合计行问题

目录 一.自定义合计行 二.合计行不展示&#xff0c;只有缩放/变大窗口或者F12弹出后台时才展示 三.合计行出现了表格滚动条下方 四.合计行整体样式的修改 五.合计行单元格样式修改 1.css 2.jsx方式 六.合计行单元格合并 一.自定义合计行 通过 show-summary 属性开启合计…

十三:java web(5)-- Spring数据持久层

目录 Spring 数据持久层 1. Spring 与 JDBC 1.1 使用 Spring 管理数据库连接 1.1.2 Apache Commons DBCP 基于配置文件xml 使用 1.1.3 Apache Commons DBCP 基于配置类使用 1.1.4 HikariCP 基于配置文件xml 使用 推荐使用 Spring Boot 默认连接池 1.1.5 HikariCP 基于配置…

初学者指南:用例图——开启您的软件工程之旅

目录 背景&#xff1a; 基本组成&#xff1a; 关联&#xff08;Assciation&#xff09;&#xff1a; 包含&#xff08;Include&#xff09;&#xff1a; 扩展&#xff08;Extend&#xff09;&#xff1a; 泛化&#xff08;Inheritance&#xff09;&#xff1a; 完整银行…

基于单片机洗衣机控制器的设计(论文+源码)

1需求分析 在智能洗衣机系统设计中&#xff0c;考虑到洗衣机在实际应用过程中&#xff0c;需要满足用户对于不同衣物清洁、消毒的应用要求&#xff0c;对设计功能进行分析&#xff0c;具体如下&#xff1a; 通过按键实现洗衣机不同工作模式的切换&#xff0c;包括标准模式&am…

qt QFontDialog详解

1、概述 QFontDialog 是 Qt 框架中的一个对话框类&#xff0c;用于选择字体。它提供了一个可视化的界面&#xff0c;允许用户选择所需的字体以及相关的属性&#xff0c;如字体样式、大小、粗细等。用户可以通过对话框中的选项进行选择&#xff0c;并实时预览所选字体的效果。Q…

Python学习从0到1 day27 第三阶段 Spark ③ 数据计算 Ⅱ

目录 一、Filter方法 功能 语法 代码 总结 filter算子 二、distinct方法 功能 语法 代码 总结 distinct算子 三、SortBy方法 功能 语法 代码 总结 sortBy算子 四、数据计算练习 需求&#xff1a; 解答 总结 去重函数&#xff1a; 过滤函数&#xff1a; 转换函数&#xff1a; 排…

今天,智谱「新清影」上线,率先进入有声视频生成时代!还要继续开源宠粉

来&#xff0c;你先把手机音量打开&#xff0c;然后去“听”下面一段视频&#xff1a; 你是不是一脸懵逼&#xff1f;不知道我想表达什么&#xff1f; 视频是AI生成的并不奇怪&#xff0c;但你可能没法相信&#xff0c;这个视频的音效&#xff0c;也是AI生成的。 火车鸣笛 你…

「Mac畅玩鸿蒙与硬件31」UI互动应用篇8 - 自定义评分星级组件

本篇将带你实现一个自定义评分星级组件&#xff0c;用户可以通过点击星星进行评分&#xff0c;并实时显示评分结果。为了让界面更具吸引力&#xff0c;我们还将添加一只小猫图片作为评分的背景装饰。 关键词 UI互动应用评分系统自定义星级组件状态管理用户交互 一、功能说明 …

pdf转excel;pdf中表格提取

一、问题描述 在工作中或多或少会遇到&#xff1a;需要将某份pdf中的表格数据提取出来&#xff0c;以便能够“修改使用”数据 可将pdf中的表格提取出来&#xff0c;解决办法还有点复杂 尤其涉及“pdf中表格不是标准的单元格”的时候&#xff0c;提取数据到excel不太容易 比…

IT架构管理

目录 总则 IT架构管理目的 明确组织与职责 IT架构管理旨在桥接技术实施与业务需求之间的鸿沟&#xff0c;通过深入理解业务战略和技术能力&#xff0c;推动技术创新以支持业务增长&#xff0c;实现技术投资的最大价值。 设定目标与范围 IT架构管理的首要目的是确立清晰的组织…

小红书图文矩阵的运营策略与引流技巧解析

内容概要 小红书图文矩阵是一种高效的内容运营方式&#xff0c;能够帮助品牌在竞争激烈的环境中脱颖而出。通过构建矩阵账号&#xff0c;品牌可以实现多维度的内容覆盖&#xff0c;创造出丰富而立体的用户体验。为什么要做图文矩阵&#xff1f;首先&#xff0c;这种方式能够提…

python中常见的8种数据结构之一元组

元组&#xff08;tuple&#xff09;是Python中常见的数据结构之一&#xff0c;它是一个有序、不可变的序列。元组使用圆括号来表示&#xff0c;可以包含任意类型的元素&#xff0c;包括数字、字符串、列表等。元组的元素可以通过索引访问&#xff0c;但是不能修改。 下面是一些…

计算机毕业设计Python+大模型动漫推荐系统 动漫视频推荐系统 机器学习 协同过滤推荐算法 bilibili动漫爬虫 数据可视化 数据分析 大数据毕业设计

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

【leetcode练习·二叉树】用「分解问题」思维解题 I

本文参考labuladong算法笔记[【强化练习】用「分解问题」思维解题 I | labuladong 的算法笔记] 105. 从前序与中序遍历序列构造二叉树 | 力扣 | LeetCode | 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵…

产品经理晋级-Axure中继器制作美观表格

这里的效果&#xff0c;步骤如下&#xff1a; 点击中继器&#xff0c;输入表格信息&#xff1b;在中继器中创建表格内容&#xff0c;把你想要的效果制作在中继器中&#xff0c;表头有几个表格&#xff0c;这边就对应多少个。 按照视频的过程把中继器双击后-样式中的文本内容&am…

防火墙|WAF|漏洞|网络安全

防火墙|WAF|漏洞|网络安全 防火墙 根据内容分析数据包&#xff1a; 1、源IP和目的IP地址 2、有效负载中的内容。 3、数据包协议&#xff08;例如&#xff0c;连接是否使用 TCP/IP 协议&#xff09;。 4、应用协议&#xff08;HTTP、Telnet、FTP、DNS、SSH 等&#xff09;。 5…