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

news2024/12/23 16:52:21

文章目录

  • 简介
  • 数据库中的存储结构
  • 数据库中的页结构
  • 从数据页来看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+树查询

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

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

相关文章

Agilent安捷伦34972A数据采集仪34908A采集卡

附加功能: 3插槽LXI数据采集单元,带6位数字数字多用表(22位)和8个插件模块可供选择(单独出售) 测量11种不同的输入信号(无外部信号调理),包括热电偶、RTD和热敏电阻的温度;DC/交流伏特或电流;2线或4线电阻;频率和周期…

FindMy技术用于滑雪板

随着冬季运动的日益普及,滑雪板作为滑雪运动的重要器材,也变得越来越受欢迎。在各大雪场和户外运动场所,人们纷纷挥舞着滑雪板,畅享冬季运动的乐趣。 在滑雪过程中,由于雪场的复杂环境和运动的高速性,很容易…

数据结构之排序

目录 ​ 1.常见的排序算法 2.插入排序 直接插入排序 希尔排序 3.交换排序 冒泡排序 快速排序 hoare版本 挖坑法 前后指针法 非递归实现 4.选择排序 直接选择排序 堆排序 5.归并排序 6.排序总结 一起去,更远的远方 1.常见的排序算法 排序:所…

Linux学习笔记-Ubuntu下ssh服务器连接异常Connection reset

文章目录 一、问题问题现象1.1 连接重置无法访问的的问题1.2 查看服务器连接状态1.3 使用调试模式查看的信息 二、临时解决方法三、从根源解决问题3.1 问题分析3.2 服务器的ssh日志3.3 修改ssh配置禁止root登录3.4 配置允许所有ip访问3.5 修改认证方法 角色:百世经纶…

自动化访客互动:提升网站效益与用户体验的关键优势

在激烈的市场竞争环境中,想抢占市场,获得收益并不容易。每一个订单的完成都要经过一定的销售周期,所以企业可以根据销售周期每个阶段的特点进行优化,留住客户。其中,企业可以在与客户在线互动的过程中,让互…

缓存一致性几种解决方案

文章目录 一、理论知识1、概述2、坏的方案2.1 先写 MySQL,再写 Redis2.2 先写 Redis,再写 MySQL2.3 先删除 Redis,再写 MySQL 3、好的方案3.1 先删除 Redis,再写 MySQL,再删除 Redis3.2 先写 MySQL,再删除 …

离散数学知识点-期末复习

目录 一、利用真值表求主析取范式、主合取范式 1.例题 二、推理证明 1.推理规则 2.例题 三、符号化命题 四、有穷集的计数 1.包含互斥原理 2.例题 ​1.文氏图法 2.包含互斥原理法 五、关系的闭包 1.三种闭包 2.Warshall算法 3.例题 六、等价关系 1.定义 2.…

杰卡德的故事

三个男人分别是杰卡德距离 杰卡德相似系数和杰卡德系数 杰卡德相似系数和杰卡德距离是互为相反数的。 杰卡德系数和杰卡德距离是不是一回事 感觉是一回事

Linux--Docker容器(最新)

这里写目录标题 安装Docker安装指令配置加速器 Docker简介名词解释作用run命令解读 操作常见命令命令的别名 数据卷简介数据卷命令使用 本地目录挂载问题发现问题解决二级目录二级目录 安装Docker 安装指令 如下文档 https://b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBkShcu…

TrustGeo代码理解(五)sublayers.py

代码链接:https://github.com/ICDM-UESTC/TrustGeo 一、导入模块 import torch import torch.nn as nn import torch.nn.functional as F 这段代码是一个简单的神经网络的定义,用于深度学习任务。 1、import torch:导入 PyTorch 库,提供张量(tensor)等深度学习操作的…

Day62力扣打卡

打卡记录 统计区间中的整数数目(动态开点线段树) 链接 class CountIntervals:__slots__ left, right, l, r, cntdef __init__(self, l1, r10 ** 9):self.left self.right Noneself.l, self.r, self.cnt l, r, 0def add(self, l: int, r: int) ->…

Spring cloud - 断路器 Resilience4J

其实文章的标题应该叫 Resilience4J,而不是Spring Cloud Resilience4J,不过由于正在对Spring cloud的一系列组件进行学习,为了统一,就这样吧。 概念区分 首先区分几个概念 Spring cloud 断路器:Spring Cloud的官网对…

Python的数据类型及举例集合、元组、列表之间的转换规则

Python语言有八种数据类型,有数字(整数、浮点数、复数)、字符串、字典、集合、元组、列表、布尔值、空值,下面我演示八种数据类型及集合、元组、列表三种类型之间的转换规则。 一、数据类型示例 下面我演示了八种数据类型&#…

Git使用rebase和merge区别

Git使用rebase和merge区别 模拟环境使用merge合并使用rebase 模拟环境 本地dev分支中DevTest增加addRole() 远程dev被同事提交增加了createResource() 使用merge合并 使用idea中merge解决冲突后, 推送远程dev后,日志图显示 使用rebase idea中使用功能rebase 解决冲突…

PyQt6 安装Qt Designer

前言:在Python自带的环境下,安装Qt Designer,并在PyCharm中配置designer工具。 在项目开发中,使用Python虚拟环境安装PyQt6-tools时,designer.exe会安装在虚拟环境的目录中:.venv\Lib\site-packages\qt6_a…

模板方法模式(行为型)

目录 一、前言 二、模板模式 三、带钩子的模板模式 四、总结 一、前言 模板方法模式是一种行为型设计模式,它定义了一个操作中的算法框架,将一些步骤延迟到子类中实现。这种模式是基于“开闭原则”的设计思想,即对扩展开放,对…

Microsoft visual studio 2013卸载方法

1、问 题 Microsoft visual studio 2013 无法通过【程序与功能】卸载 2、解决方法 使用微软的Microsoft visual studio 2013 专用卸载工具 工具下载链接:https://github.com/Microsoft/VisualStudioUninstaller/releases 或 链接:https://pan.baidu.c…

分布式事务seata使用示例及注意事项

分布式事务seata使用示例及注意事项 示例说明代码调用方(微服务A)服务方(微服务B) 测试测试一 ,seata发挥作用,成功回滚!测试二:处理feignclient接口的返回类型从Integer变成String,…

数理统计基础:参数估计与假设检验

在学习机器学习的过程中,我充分感受到概率与统计知识的重要性,熟悉相关概念思想对理解各种人工智能算法非常有意义,从而做到知其所以然。因此打算写这篇笔记,先好好梳理一下参数估计与假设检验的相关内容。 1 总体梳理 先从整体结…

OceanBase数据库初识

文章目录 说明分布式数据库发展发展历史OceanBase和传统数据库的对比总结 OceanBase数据库产品简介应用案例 OceanBase数据库产品OceanBase数据库内核OceanBase开发者中心(ODC)产品架构OMS核心功能简介 说明 本文仅供学习和交流学习内容参考官方的培训资…