SQL进阶理论篇(七):B+树的查询及存储机制

文章目录

  • 简介
  • 数据库中的存储结构
  • 数据库中的页结构
  • 从数据页来看B+树的查询过程
  • 总结
  • 参考文献

简介

我们之前已经了解过数据库的B+树索引和Hash索引,这些索引信息以及数据记录都是保存在文件里的,确切的说是存储在页结构中。

本节,从我们将了解数据库的存储结构以及底层页结构的原理,从而加深我们对索引运行机制的认识。

主要包括以下几部分:

  • 数据库中的存储结构是怎样的,页、区、段和表空间分别是指什么?
  • 为什么页(Page)是数据库存储空间的基本单位?
  • 从数据页的角度来看,B+树是如何进行查询的?

数据库中的存储结构

数据库中的记录是按照行来存储的,但是数据库的读取并不是以行为单位,否则一次读取(即一次IO)只能读一行,那整个查询的读取效率也太感人了。

因此在数据库中,每次读取其实是读取一页(Page)。不论是读一行,还是读多行,都是将这些行所在的页进行加载读取的过程。就是说,数据库管理存储的基本单位,是页(Page)

而一个页中,可以存储多个行记录,即Row。

同时,在数据库中,还存在着区(Extent)、段(Segment)和表空间(Tablespace)。

行、页、区、段、表空间的关系如下图:

在这里插入图片描述

由上图可见,一个表空间包含多个段,一个段包含多个区,一个区包含多个页,而页,又是由一个个行组成的。

下面简单介绍下这些概念。

区,即Extent,是比页大一级的存储结构。在InnoDB存储引擎中,一个区会被分配64个连续的页。由于InnoDB中一个页的默认大小是16KB,所以一个区的大小是64*16KB=1MB。

段,即Segment,是区上的一级存储结构。区在文件系统是一个连续分配的空间,但是段不用,段中并不要求区与区之间是相邻的。段是数据库中的分配单位,不同数据库对象以不同的段形式存在着。当我们在创建数据表时,就会创建一个表段。当我们在创建索引的时候,就会创建一个索引段。

表空间(TableSpace)是一个逻辑容器。表空间存储的对象是段,一个表空间由一至多个段组成,一个段只能属于一个表空间。而整个数据库,是由一个或多个表空间组成,从管理上来讲,表空间可以划分为系统表空间、用户表空间、撤销表空间和临时表空间等。

InnoDB中有两种表空间:共享表空间和独立表空间。

共享表空间是指多张表可以共用一个表空间,独立表空间是指每张表有一个独立的表空间,这个表空间将只用于维护这张表自己的数据和索引信息。独立的表空间意味着可以很方便的在不同的数据库之间进行单表的迁移。

可以通过以下命令来查看InnoDB的表空间类型:

show variables like 'innodb_file_per_table';

输出为:

在这里插入图片描述

on表示每张表都会被单独保存为一个.bid文件,即启用的是独立表空间。

数据库中的页结构

页,如果按照类型划分的话,常见的有数据页(保存B+树节点)、系统页、Undo页和事务数据页等。其中数据页是我们最常使用的页。

单个表页的大小限定了表行的最大长度,不同DBMS的默认表页大小不同。比如在MySQL的InnoDB中,默认页大小是16KB,可以通过以下命令查看:

show variables like '%innodb_page_size%';

在这里插入图片描述

而SQL Server中页大小是8KB,在Oracle中以术语"块",即block,来代表页,其支持的块大小为2KB、4KB、8KB、16KB、32KB和64KB。

页同时也是数据库中IO操作的最小单位,每一次IO操作都至少读取一页,这里面存储了数据库相关的内容。

数据页包括了七个部分,分别是:

  • 文件头File Header;
  • 页头(Page Header);
  • 最大最小记录(Infimum + supremum);
  • 用户记录(User Records)
  • 空闲空间(Free Space)
  • 页目录(Page Directory)
  • 文件尾(File Tailer)

其示意图如下:

在这里插入图片描述

这7部分都有啥作用呢?教程里给简单总结了下:

在这里插入图片描述

实际上,这7部分可以归为3类:

  • 文件通用部分
  • 记录部分
  • 索引部分

文件通用部分,就是文件头和文件尾,二者一前一后对一个页的内容进行封装,并且可以通过文件头和文件尾校验的方式,来确保页的传输是完整的。

在文件头上有两个字段,分别是FIL_PAGE_PREV和PIL_PAGE_NEXT,它们的作用相当于是指针,分别指向上一个数据页和下一个数据页。通过指针连起来的数据页相当于是一个双向链表,如图:

在这里插入图片描述

这些需要注意,采用链表这种结构之后,数据页之间就不需要是物理上的连续了,而是保证逻辑上的连续就可以。

文件头的作用讲完了,再讲讲文件尾的作用。

文件尾的最大作用,就是配合文件头,来校验一个页的数据完整性。举个例子,当我们进行页传输的时候,突然断电了,那么当前页大概率传输的不完整。那我怎么确认它是不是完整呢?

这时候通过文件尾的校验和(checksum,如MD5算法)与文件头的校验和做比对,如果两个值不相等,证明传输的有问题,需要进行重新传输,否则就认为当前页传输是正常完成。

记录部分,这部分是页的核心部分,毕竟页的主要作用就是用来存储。它包括了最大最小记录、用户记录、空闲空间。

其中,最大最小记录和用户记录占据了页结构的主要空间,至于空闲空间,顾名思义,当有新记录插进来的时候,就会从空闲空间里分配空间用于存储新纪录。这个过程,教程里也贴了图了,如下:

在这里插入图片描述

索引部分,其实主要指的是页目录,它起到了记录的索引的作用。

在页中,记录是以单向链表的形式进行存储的。单向链表的插入和删除都很方便,但就是检索很费劲,需要一条一条按顺序遍历检索,运气不好的话得把整个链表遍历一遍之后才能找到想要的值。

因此,在页目录中提供了二分查找的方式,用来提高对记录的查询效率,这个过程就相当于是给记录创建了一个目录,所以叫做页目录。

那么页目录是怎么形成的呢?

  1. 将所有记录分成几个组,这些记录包括最小记录和最大记录,但不包括标记为"已删除"的记录。
  2. 第1组,就是最小记录所在的分组,只保留一条记录;最后一组,就是最大记录所在的分组,会有1-8条记录;其余的组,记录的数量在4-8条之间。这样做的好处是,除了第1组之外,其余组的记录会尽量平分,保证查找效率的稳定性(保持每组数据差不多)。
  3. 在每组最后一条记录的头信息中,会存储该组一共有多少条记录,作为n_owned字段。
  4. 页目录里存储的是每组最后一条记录的地址偏移量(也被称为"槽",即slot),这些偏移量会按照先后顺序存储起来。其实还是指针的概念,每个槽相当于是指向对应组的最后一条记录的指针。

整个过程图示如下:

在这里插入图片描述

照这么看,页目录其实就是一堆指针的结合体,说它是索引并不为过。

那为什么说我们通过页目录来查找数据时,是在做二分查找呢?

还是以上图为例,假设我想查找主键为9的记录。

首先我找到槽的中间位置,即(0+4)/ 2 = 2,所以先定位到槽2,槽2对应的是分组3里的最后一条记录,我们从中取出主键数值是8。因为9大于8,所以接下来我们需要在槽编号为(2,4]范围内再度查找。

再次定位中间位置,即(2+4)/2=3,所以定位到槽3,槽3对应的是分组4里的最后一条记录,我们从中取出主键数值是12,所以下一步应该从槽3中进行查找。

遍历槽3中的所有记录,找到关键字为9的记录,取出该记录行的内容,本次查找结束。

可以看到,这就是二分查找的过程,或者严格来讲,是二分查找 + 局部遍历。

从数据页来看B+树的查询过程

MySQL的InnoDB引擎采用的是B+树作为索引,而索引可以分为聚集索引和非聚集索引(二级索引,指索引里存储的是指针,而不是数据行),这些索引都相当于是一棵B+树。

一棵B+树按照节点类型可以分为两部分:

  • 叶子节点:B+树最底层的节点,高度为0,存储行记录;
  • 非叶子节点:节点高度大于0,存储索引键和页面指针,并不存储行记录本身。

如图:

在这里插入图片描述

在一棵B+树中,每个节点都是一个页

同一层的节点之间,通过页的结构(文件头中的两个指针)构成一个双向链表

非叶子节点,包括了多个索引行,每个索引行存储索引键和指向下一层页面的页面指针。

叶子节点,存储了关键字和行记录。在节点内部(即页内部),记录是一个单向链表,可以通过页目录采用二分查找的方式来查找内部的记录。

所以,B+树进行记录检索的完整流程,其实是这样的:

首先从根节点开始,逐层搜索,直到找到叶子节点,即对应的数据页。在确定了待查找数据就存在于这个数据页上之后,我们将这个数据页加载到内存,通过页目录做二分查找,定位出一个粗略的记录分组,最后在这个分组里通过链表遍历的方式来找到指定记录行。

那么,普通索引和唯一性索引,在查找效率上有什么不同呢?

过程确实有不同,但是绝大多数情况下,其实检索效率不会有太大差别。

唯一索引就是在普通索引上增加了唯一性约束,也就是说关键字是唯一的,只要找到了待查找关键字之后就可以停止检索。

但普通索引不行,由于可能会重复,所以会存在多条满足情况的记录行。所以在找到对应的数据页之后,不能只查找一次就完事了,需要沿着链表多往下走几次,直到把满足情况的记录行都挑出来。这个过程是在内存中进行的,一般不会额外再读取其他页,所以对CPU来讲,消耗的这点时间属于是洒洒水啦。

因此,大部分情况下,二者是没有 明显差别的。

总结

同一棵树上,同一层的页之间采用双向链表连接,而在页内部,记录之间是采用单向链表的形式。

参考文献

  1. 27丨从数据页的角度理解B+树查询

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

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

相关文章

【LeetCode刷题】-- 161.相隔为1的编辑距离

161.相隔为1的编辑距离 方法:一次遍历 首先,我们要确认字符串的长度不会相差太远。如果长度差了2个或更多字符,那么 s 和 t 就不可能是一次编辑之差的字符串。 接下来,我们假设 s 的长度总是短于或等于 t 的长度。如果不是这样&…

cesium 自定义贴图,shadertoy移植教程。

1.前言 cesium中提供了一些高级的api,可以自己写一些shader来制作炫酷的效果。 ShaderToy 是一个可以在线编写、测试和分享图形渲染着色器的网站。它提供了一个图形化的编辑器,可以让用户编写基于 WebGL 的 GLSL 着色器代码,并实时预览渲染结…

大数据存储技术(3)—— HBase分布式数据库

目录 一、HBase简介 (一)概念 (二)特点 (三)HBase架构 二、HBase原理 (一)读流程 (二)写流程 (三)数据 flush 过程 &#xf…

配置802.1x认证

实验目的: 某公司拥有两个部门,市场部和人事部门,市场部和人事部的IP地址分别为10.1.11.0/24、10.1.21.0/24两个IP网段。市场部属于vlan11,人事部属于vlan21。现在需要在SW2上配置802.1x认证,实现终端用于只有认证成功…

BKP 备份寄存器 RTC 实时时钟-stm32入门

这一章节我们要讲的主要内容是 RTC 实时时钟,对应手册,是第 16 章的位置。 实时时钟这个东西,本质上是一个定时器,但是这个定时器,是专门用来产生年月日时分秒,这种日期和时间信息的。所以学会了 STM32 的…

Flink系列之:SQL提示

Flink系列之:SQL提示 一、动态表选项二、语法三、例子四、查询提示五、句法六、加入提示七、播送八、随机散列九、随机合并十、嵌套循环十一、LOOKUP十二、进一步说明十三、故障排除十四、连接提示中的冲突案例十五、什么是查询块 SQL 提示可以与 SQL 语句一起使用来…

【卡塔尔世界杯数据可视化与新闻展示】

卡塔尔世界杯数据可视化与新闻展示 前言数据获取与处理可视化页面搭建功能实现新闻信息显示详情查看登录注册评论信息管理 创新点结语 前言 随着卡塔尔世界杯的临近,对于足球爱好者来说,对比赛的数据分析和新闻报道将成为关注的焦点。本文将介绍如何使用…

腾讯云优惠全站搜——云服务器配置大全精准

腾讯云推出优惠全站搜页面 https://curl.qcloud.com/PPrF9NFe 在这个页面可以一键查询所需云服务器、轻量应用服务器、数据库、存储、CDN、网络、安全、大数据等云产品优惠活动大全,活动打开如下图: 腾讯云优惠全站搜——优惠合集 如上图,在这…

uniGUI学习之UniTreeview

UniTreeview中能改变一级目录的字体和颜色 function beforeInit(sender, config) { ID"#"config.id; Ext.util.CSS.createStyleSheet( ${ID} .x-tree-node-text{color:green;font-weight:800;} ${ID} .x-tree-elbow-line ~ span{color:black;font-weight:400;} ); }

macbookpro 2024怎么恢复出厂设置

可能你的MacBook曾经是高性能的代表,但是现在它正慢慢地逝去了自己的光芒?随着逐年的使用以及文件的添加和程序的安装,你的MacBook可能会开始变得迟缓卡顿,或者失却了以往的光彩。如果你发现你的Mac开始出现这些严重问题&#xff…

Windows设备管理

1、前言 熟悉Windows系统的都应该使用过设备管理器。设备管理器将操作系统中所有已安装的设备分类展现出来。同时提供了安装、卸载、启用和禁用的功能。 那么,我们应该如何通过C编程的方式实现这种功能呢?答案很简单,那就是使用SetupDi函数族…

GZ015 机器人系统集成应用技术样题4-学生赛

2023年全国职业院校技能大赛 高职组“机器人系统集成应用技术”赛项 竞赛任务书(学生赛) 样题4 选手须知: 本任务书共 25页,如出现任务书缺页、字迹不清等问题,请及时向裁判示意,并进行任务书的更换。参赛队…

2020 ICPC·小米邀请赛 决赛 J. Rikka with Book(状压dp)

题目 登录—专业IT笔试面试备考平台_牛客网 n(n<20)本书&#xff0c;放在桌子上&#xff0c; 第i本书的可以看成是li(li<1e3)*1*1的物体&#xff0c;其中长为li&#xff0c;宽为1&#xff0c;高为1&#xff0c; 质量均匀分布&#xff0c;且为wi(wi<1e3) 求n本书摞…

前端桌面通知(Desktop Notifications)API

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

msvcrtd.dll下载安装方法,解决msvcrtd.dll找不到的问题

在这篇文章中&#xff0c;我们将详细讨论msvcrtd.dll文件的下载安装方法&#xff0c;并分析出现找不到msvcrtd.dll的情况及解决方法。如果你遇到了与msvcrtd.dll相关的问题&#xff0c;本文将为你提供全面且详细的解决方案。 一.什么是msvcrtd.dll文件 首先&#xff0c;让我们…

JS中的String常用的实例方法

splice():分隔符 把字符串以分隔符的形式拆分为数组 const str pink,red;const arr str.split(,);console.log(arr);//Array[0,"pink";1:"red"]const str1 2022-4-8;const arr1 str1.split(-);console.log(arr1);//Array[0,"2022";1:"…

权重衰减(Weight Decay)

在深度学习中&#xff0c;权重衰减&#xff08;Weight Decay&#xff09;是一种常用的正则化技术&#xff0c;旨在减少模型的过拟合现象。权重衰减通过向损失函数添加一个正则化项&#xff0c;以惩罚模型中较大的权重值。 一、权重衰减 在深度学习中&#xff0c;模型的训练过程…

leetcode移除元素

移除元素 题目分析题解代码:c版本c版本 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺…

六大场景36种数据分析模型及方法图示,数据分析师必备!

【关注微信公众号&#xff1a;跟强哥学SQL&#xff0c;回复“笔试”免费领取大厂SQL笔试题。】 我一直认为&#xff0c;实际工作中&#xff0c;精通数据分析工具仅仅只是数据思维训练的一部分&#xff0c;掌握丰富的数据分析方法和模型实际上更为重要。 基于科学的数据分析方法…

『PyTorch』张量和函数之gather()函数

文章目录 PyTorch中的选择函数gather()函数 参考文献 PyTorch中的选择函数 gather()函数 import torch a torch.arange(1, 16).reshape(5, 3) """ result: a [[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12],[13, 14, 15]] """# 定义两个index…