从数据页的角度看 B+ 树

news2024/9/19 19:25:46

资料来源 : 小林coding

小林官方网站 : 小林coding (xiaolincoding.com)

大家背八股文的时候,都知道 MySQL 里 InnoDB 存储引擎是采用 B+ 树来组织数据的。

这点没错,但是大家知道 B+ 树里的节点里存放的是什么呢?查询数据的过程又是怎样的?

这次,我们从数据页的角度看 B+ 树,看看每个节点长啥样。

InnoDB 是如何存储数据的?

MySQL 支持多种存储引擎,不同的存储引擎,存储数据的方式也是不同的,我们最常使用的是 InnoDB 存储引擎,所以就跟大家图解下InnoDB 是如何存储数据的。

记录是按照行来存储的,但是数据库的读取并不以「行」为单位,否则一次读取(也就是一次 I/O 操作)只能处理一行数据,效率会非常低。

因此,InnoDB 的数据是按「数据页」为单位来读写的,也就是说,当需要读一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。

数据库的 I/O 操作的最小单位是页,InnoDB 数据页的默认大小是 16KB,意味着数据库每次读写都是以 16KB 为单位的,一次最少从磁盘中读取 16K 的内容到内存中,一次最少把内存中的 16K 内容刷新到磁盘中。

数据页包括七个部分,结构如下图:

这 7 个部分的作用如下图 : 

在 File Header 中有两个指针,分别指向上一个数据页和下一个数据页,连接起来的页相当于一个双向的链表,如下图所示:

采用链表的结构是让数据页之间不需要是物理上的连续的,而是逻辑上的连续。

数据页的主要作用是存储记录,也就是数据库的数据,所以重点说一下数据页中的 User Records 是怎么组织数据的。

数据页中的记录按照「主键」顺序组成单向链表,单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索。

因此,数据页中有一个页目录,起到记录的索引作用,就像我们书那样,针对书中内容的每个章节设立了一个目录,想看某个章节的时候,可以查看目录,快速找到对应的章节的页数,而数据页中的页目录就是为了能快速找到记录。

那 InnoDB 是如何给记录创建页目录的呢?页目录与记录的关系如下图:

页目录创建的过程如下:

页目录创建的过程如下:

  1. 将所有的记录划分成几个组,这些记录包括最小记录和最大记录,但不包括标记为“已删除”的记录;
  2. 每个记录组的最后一条记录就是组内最大的那条记录,并且最后一条记录的头信息中会存储该组一共有多少条记录,作为 n_owned 字段(上图中粉红色字段)
  3. 页目录用来存储每组最后一条记录的地址偏移量,这些地址偏移量会按照先后顺序存储起来,每组的地址偏移量也被称之为槽(slot),每个槽相当于指针指向了不同组的最后一个记录

从图可以看到,页目录就是由多个槽组成的,槽相当于分组记录的索引。然后,因为记录是按照「主键值」从小到大排序的,所以我们通过槽查找记录时,可以使用二分法快速定位要查询的记录在哪个槽(哪个记录分组),定位到槽后,再遍历槽内的所有记录,找到对应的记录,无需从最小记录开始遍历整个页中的记录链表。

以上面那张图举个例子,5 个槽的编号分别为 0,1,2,3,4,我想查找主键为 11 的用户记录:

  • 先二分得出槽中间位是 (0+4)/2=2 ,2号槽里最大的记录为 8。因为 11 > 8,所以需要从 2 号槽后继续搜索记录;
  • 再使用二分搜索出 2 号和 4 槽的中间位是 (2+4)/2= 3,3 号槽里最大的记录为 12。因为 11 < 12,所以主键为 11 的记录在 3 号槽里;
  • 这里有个问题,「槽对应的值都是这个组的主键最大的记录,如何找到组里最小的记录」?比如槽 3 对应最大主键是 12 的记录,那如何找到最小记录 9。解决办法是:通过槽 3 找到 槽 2 对应的记录,也就是主键为 8 的记录。主键为 8 的记录的下一条记录就是槽 3 当中主键最小的 9 记录,然后开始向下搜索 2 次,定位到主键为 11 的记录,取出该条记录的信息即为我们想要查找的内容。

看到第三步的时候,可能有的同学会疑问,如果某个槽内的记录很多,然后因为记录都是单向链表串起来的,那这样在槽内查找某个记录的时间复杂度不就是 O(n) 了吗?

这点不用担心,InnoDB 对每个分组中的记录条数都是有规定的,槽内的记录就只有几条:

  • 第一个分组中的记录只能有 1 条记录;
  • 最后一个分组中的记录条数范围只能在 1-8 条之间;
  • 剩下的分组中记录条数范围只能在 4-8 条之间。

B+ 树是如何进行查询的?

上面我们都是在说一个数据页中的记录检索,因为一个数据页中的记录是有限的,且主键值是有序的,所以通过对所有记录进行分组,然后将组号(槽号)存储到页目录,使其起到索引作用,通过二分查找的方法快速检索到记录在哪个分组,来降低检索的时间复杂度。

但是,当我们需要存储大量的记录时,就需要多个数据页,这时我们就需要考虑如何建立合适的索引,才能方便定位记录所在的页。

为了解决这个问题,InnoDB 采用了 B+ 树作为索引。磁盘的 I/O 操作次数对索引的使用效率至关重要,因此在构造索引的时候,我们更倾向于采用“矮胖”的 B+ 树数据结构,这样所需要进行的磁盘 I/O 次数更少,而且 B+ 树 更适合进行关键字的范围查询。

InnoDB 里的 B+ 树中的每个节点都是一个数据页,结构示意图如下:

通过上图,我们看出 B+ 树的特点:

  • 只有叶子节点(最底层的节点)才存放了数据,非叶子节点(其他上层节)仅用来存放目录项作为索引。
  • 非叶子节点分为不同层次,通过分层来降低每一层的搜索量;
  • 所有节点按照索引键大小排序,构成一个双向链表,便于范围查询;

我们再看看 B+ 树如何实现快速查找主键为 6 的记录,以上图为例子:

  • 从根节点开始,通过二分法快速定位到符合页内范围包含查询值的页,因为查询的主键值为 6,在[1, 7)范围之间,所以到页 30 中查找更详细的目录项;
  • 在非叶子节点(页30)中,继续通过二分法快速定位到符合页内范围包含查询值的页,主键值大于 5,所以就到叶子节点(页16)查找记录;
  • 接着,在叶子节点(页16)中,通过槽查找记录时,使用二分法快速定位要查询的记录在哪个槽(哪个记录分组),定位到槽后,再遍历槽内的所有记录,找到主键为 6 的记录。

可以看到,在定位记录所在哪一个页时,也是通过二分法快速定位到包含该记录的页。定位到该页后,又会在该页内进行二分法快速定位记录所在的分组(槽号),最后在分组内进行遍历查找。

聚簇索引和二级索引

另外,索引又可以分成聚簇索引和非聚簇索引(二级索引),它们区别就在于叶子节点存放的是什么数据:

  • 聚簇索引的叶子节点存放的是实际数据,所有完整的用户记录都存放在聚簇索引的叶子节点;
  • 二级索引的叶子节点存放的是主键值,而不是实际数据。

因为表的数据都是存放在聚簇索引的叶子节点里,所以 InnoDB 存储引擎一定会为表创建一个聚簇索引,且由于数据在物理上只会保存一份,所以聚簇索引只能有一个。

InnoDB 在创建聚簇索引时,会根据不同的场景选择不同的列作为索引:

  • 如果有主键,默认会使用主键作为聚簇索引的索引键;
  • 如果没有主键,就选择第一个不包含 NULL 值的唯一列作为聚簇索引的索引键;
  • 在上面两个都没有的情况下,InnoDB 将自动生成一个隐式自增 id 列作为聚簇索引的索引键;

一张表只能有一个聚簇索引,那为了实现非主键字段的快速搜索,就引出了二级索引(非聚簇索引/辅助索引),它也是利用了 B+ 树的数据结构,但是二级索引的叶子节点存放的是主键值,不是实际数据。

二级索引的 B+ 树如下图,数据部分为主键值:

因此,如果某个查询语句使用了二级索引,但是查询的数据不是主键值,这时在二级索引找到主键值后,需要去聚簇索引中获得数据行,这个过程就叫作「回表」,也就是说要查两个 B+ 树才能查到数据。不过,当查询的数据是主键值时,因为只在二级索引就能查询到,不用再去聚簇索引查,这个过程就叫作「索引覆盖」,也就是只需要查一个 B+ 树就能找到数据。

总结

InnoDB 的数据是按「数据页」为单位来读写的,默认数据页大小为 16 KB。每个数据页之间通过双向链表的形式组织起来,物理上不连续,但是逻辑上连续。

数据页内包含用户记录,每个记录之间用单向链表的方式组织起来,为了加快在数据页内高效查询记录,设计了一个页目录,页目录存储各个槽(分组),且主键值是有序的,于是可以通过二分查找法的方式进行检索从而提高效率。

为了高效查询记录所在的数据页,InnoDB 采用 b+ 树作为索引,每个节点都是一个数据页。

如果叶子节点存储的是实际数据的就是聚簇索引,一个表只能有一个聚簇索引;如果叶子节点存储的不是实际数据,而是主键值则就是二级索引,一个表中可以有多个二级索引。

在使用二级索引进行查找数据时,如果查询的数据能在二级索引找到,那么就是「索引覆盖」操作,如果查询的数据不在二级索引里,就需要先在二级索引找到主键值,需要去聚簇索引中获得数据行,这个过程就叫作「回表」。


这期就到这里 , 下期见!

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

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

相关文章

【进程概念】Linux进程状态 | 僵尸进程 | 孤儿进程

目录 Linux中的进程状态 R运行状态&S休眠状态 T/t停止状态stopped(tracing stop) D磁盘休眠状态Disk sleep X死亡状态dead&Z僵尸状态zombie 僵尸进程 僵尸进程的理解 演示僵尸进程 僵尸进程的危害 孤儿进程 孤儿进程的理解 演示孤儿进程 进程状态的查看…

简化业务流程,AppLink连接一定签

APPlink是什么 APPlink是RestCloud打造的一款简单易用的零代码自动化集成平台&#xff0c;为业务流程提供自动化的解决方案&#xff0c;将企业内部的核心系统以及第三方应用程序和云服务等进行集成。无论是开发人员还是业务人员&#xff0c;都可以使用APPlink轻松构建出高效、…

锁的7大分类

锁 首先会了解锁的整体概念&#xff0c;了解锁究竟有哪些分类的标准。在后面的文章中会对重要的锁进行详细的介绍。 锁的7大分类 需要首先指出的是&#xff0c;这些多种多样的分类&#xff0c;是评价一个事物的多种标准&#xff0c;比如评价一个城市&#xff0c;标准有人口多…

鸿蒙Harmony应用开发—ArkTS-高级组件:@ohos.advertising.AdComponent (非全屏广告展示组件))

本模块提供展示非全屏广告的能力。 说明&#xff1a; 本模块首批接口从API Version 11开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import { AdComponent } from ohos.advertising.AdComponent; AdComponent AdComponent(ads: Ar…

基于springboot+vue+Mysql的校园交友网站

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

基于Java校园跑腿管理系统设计与实现(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

OSCP靶场--Crane

OSCP靶场–Crane 考点(CVE-2022-23940sudo service提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.229.146 -sC -sV --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-25 08:07 EDT Nmap scan report for 192.16…

项目中如何进行限流(限流的算法、实现方法详解)

❤ 作者主页&#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是李奕赫&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习!!!&#x1f389;&#x1f389; 文章目录 限流的算法漏…

jdk api之UnsupportedEncodingException基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

力扣HOT100 - 49. 字母异位词分组

解题思路&#xff1a; 排序 注意&#xff1a; 返回时不能用List&#xff0c;因为List是抽象类&#xff0c;return的必须是List的具体实现&#xff0c;如ArrayList class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, Lis…

哈希存储、哈希表、哈希表的基本操作、算法的一些概念

我要成为嵌入式高手之3月25日数据结构第七天&#xff01;&#xff01; ————————————————————————————— 搜索(查找)二叉树 思想&#xff1a;左大右小 主要为了进行二分查找&#xff0c;由于根节点选择不合适&#xff0c;容易造成树不平衡&#…

NOMA免调度接入技术

标题 系统模型 参考视频&#xff1a;添加链接描述 利用接收机的复杂度提升为代价&#xff0c;提升频谱效率。为了保证上行方向上面&#xff0c;能够接入更多的用户&#xff0c;NOMA的根本思路&#xff0c;就是让多个用户占用相同的资源进行上行传输 系统模型 采用TDMA的方式…

K8s-网络原理-下篇

引言 本文是《深入剖析 K8s》的学习笔记&#xff0c;相关图片和案例可从https://github.com/WeiXiao-Hyy/k8s_example中获取&#xff0c;欢迎Star! K8s 的网络隔离: NetWorkPolicy K8s 如何考虑容器之间网络的“隔离” -> NetWorkPolicy 以下是一个 NetWorkPolicy 的定义…

msvcp110.dll丢失修复办法

在计算机使用过程中&#xff0c;我们经常会遇到一些扩展名为.dll的文件&#xff0c;这些文件是动态链接库文件&#xff0c;用于提供程序运行时所需的函数和资源。其中&#xff0c;msvcp110.dll文件是一个非常重要的动态链接库文件&#xff0c;它属于Microsoft Visual C 2012 Re…

【vue核心技术实战精讲】1.3 - 1.5 VUE 指令 (上)

文章目录 前言 本节内容1、v-text 和 v-html代码效果 2、v-if 和 v-show代码效果 3、v-bind3.1、用法&#xff1a;v-bind: 属性 &#xff0c;简写 :3.2、动态 attribute 名效果 3.3、内联字符串拼接效果 3.4、绑定 class效果 3.5、style 绑定3.6、绑定一个全是 attribute 的对…

学点儿Java_Day10_集合框架(List、Set、HashMap)

1 简介 ArrayList: 有序(放进去顺序和拿出来顺序一致)&#xff0c;可重复 HashSet: 无序(放进去顺序和拿出来顺序不一定一致)&#xff0c;不可重复 Testpublic void test1() {String[] array new String[3];//List: 有序 可重复//有序: 放入顺序 与 拿出顺序一致&#xff0c;…

精酿啤酒:酿造工艺的传承与改进

啤酒酿造工艺是一种历史悠久且不断发展的技艺&#xff0c;它随着时代的变化和技术的进步不断得到改进和创新。Fendi Club啤酒作为一家精酿啤酒品牌&#xff0c;在传承经典酿造工艺的同时&#xff0c;也不断探索和改进&#xff0c;以满足现代消费者的需求。 Fendi Club啤酒传承了…

蓝桥杯物联网遇见的重大BUG及其产生原因和解决方法

BUG列表 1、ADC的RP2显示一直为0&#xff1a;2、LORX_Tx发送数据乱码&#xff1a;3、strcmp比较char a[2] {1, 2}与“12”字符串是否相等板子会死机&#xff1a;4、LORA_Tx和LORA_Rx放一起会接收不到数据&#xff1a;5、RTC获取到静止时间&#xff1a;6、ADC获取RP1和RP2模拟量…

【k8s网络】梳理cni发展脉络

参考 《深入剖析 Kubernetes&#xff08;张磊&#xff09;》 补充 详解 Calico 三种模式&#xff08;与 Fannel 网络对比学习&#xff09;_calico vxlan-CSDN博客 容器网络 容器的网络栈 每个容器有自己的 net namespace net namespace 可以称之为网络栈所谓“网络栈”&…

睿考网:不是会计专业能考中级会计师吗?

不是会计专业也是可以考中级会计师的&#xff0c;中级会计师报名条件中并没有对专业做明确的限制&#xff0c;不同的学历对工作年限的要求不一样&#xff0c;如果考生满足报考条件就可以参加。 1.具备大学专科学历&#xff0c;从事会计工作满5年。 2.具备大学本科学历或学士学…