数据库磁盘文件格式的设计原理内幕

news2025/4/19 8:42:44

引言

  访问磁盘需通过系统调用来实现,因此通常我们需要指定目标文件的偏移量,然后把数据从磁盘上的形式解析成合适主存的形式。这意味着要想设计一个高效的磁盘数据结构,必须构造一种易于修改和解析的文件格式。在本文中,我们将讨论通用的原理和实践,它们可以帮助我们设计各种磁盘结构,而不仅仅是B树。
  磁盘数据结构中指针管理的语义与内存中的有所不同,你可以将磁盘上的B树看做是一种页管理机制:算法需要组合页并在页中移动。需要计算页和指向它们的指针并将它们放在相应位置。

1 动机

  构建一种文件格式十分类似于在非托管内存模型的语言中构建数据结构。我们先分配一个数据块,然后使用定长的原始数据类型和结构体以设计者希望的方式将其切片;如果想要引用一大块内存或者是变长的数据结构,则可以使用指针。
非托管内存模型的语言允许我们在需要时分配更多的内存,而不必考虑诸如是否有连续的内存段、这些内存是分段的还是连续的、内存释放之后会怎样等问题。而在磁盘上,我们必须自己处理垃圾收集和碎片问题。
  当数据存在于主存时,大部分内存布局的问题都不存在、很容易解决或是能用第三方库来解决。例如,处理变长字段和超大数据就很简单;只要分配内存并使用指针指向它即可,无须以任何特殊的方式对其进行布局。在某些情况下,开发者需要设计出针对应用的特殊内存数据结构以利用CPU缓存行、预取或者硬件等相关特性,但这样做主要是出于优化的目的。

2 二进制编码

  为了将数据结构高效地保存至磁盘中,我们需将它编码成一种紧凑、易于序列化与反序列化的格式。由于不能使用malloc与free等操作,只能使用read和write,所以需要用不同的思路重新思考数据访问的方式,并且据此来准备数据。在本小节中,将讨论构建高效页面布局的主要原理,这些原理适用于任何二进制格式,后续可以利用类似的指导原则来构建文件、序列化格式或通信协议。
在将数据记录组织成页之前,我们首先需要了解这些问题:如何以二进制形式表示键和数据记录,如何将多个值组合成更复杂的结构,以及如何实现可变长度的数据类型和数组。

2.1 原始类型

  键和值具有某种类型,比如int(整数)、date(日期)或string(字符串)等,并且可利用二进制形式表示(序列化为二进制形式和从二进制反序列化)。大多数的数值类型都用固定大小的值表示。当处理多字节数值时,务必在解码时使用相同的字节序,字节序决定了一组字节的先后顺序。
大端 从最高有效字节开始,从高位到低位依次排序。换句话说,最高有效字节具有最低的地址。
小端 从最低有效字节开始,从低位到高位依次排序。

下图解释了大端与小端的差异,对于十六进制的32为整数0xABCDEFGH,其中AB是最高有效字节,以下是分别用大端和小端字节编码的结果。

在这里插入图片描述
  数据记录由数值、字符串、布尔值之类的原始数据以及它们的组合构成。但是,当通过网络传输或者是将其存储到磁盘上时,我们只能使用字节序列。这意味着,当我们发送或写入一条记录时必须先将其序列化(转换成一段可解释的字节序列),当我们发送或读取一条记录时必须先将其反序列化(将字节序列解析为原来的记录)。

2.2 字符串和变长数据

  所有原始数值类型都有固定大小。构造更复杂数值类型类似于C语言中结构体struct,我们可以将任何原始数据类型组合到结构体中,并使用固定长度的数组或者指针指向其他内存区域。
字符串和其他变长数据类型(定长类型数组)可以序列化为一个表示长度的数值字段size,加上size字节(实际数据)。如tuple结构体所示:

typedef struct tuple
{
	size  uint32;
	char  data[size];
}tuple;

该结构具有如下优点:能够在常数时间内获取字符串的长度而无需遍历整个字符串

2.3 功能多样的布尔值、枚举值和标记

  布尔值可以用单个字节表示,也可以将true和false分别表示1和0。由于布尔值只有两个值,用一个完整的字节过于浪费,所以有的开发者常将每8个布尔值合成一批,每个布尔值占一位。我们称1所在的比特位已置位,0所在的比特位未置位。
枚举值可以被表示为整数,常被用于二进制和通信协议。枚举值用于表示重复多、基数少的值。例如用枚举值表示B树节点类型:

enum Node_type
{
	ROOT_NODE, 
	INTERNAL_NODE,
	LEFT_NODE
};

  另一个密切相关的概念是标记,它是打包的布尔值和枚举值的组合。标记可以表示多个非互斥的布尔值参数,例如可以用它表示元组中某个字段是定长还是变长、元组是否被施加锁等信息。由于每个比特位都代表一个标记值,所以我们只能将2的幂用作为掩码。
在这里插入图片描述

像布尔值一样,我们可以利用位掩码和位运算符从打包的值中读写各个标记位。例如,要想将其中某个标记位置为1,可以将它和相应的位掩码求或,也可以用移位运算和位的下标代替位掩码。要想将某一位置为0,可以利用按位与和按位取反运算符。为了测试第n位是否被置位,可以将按位与的结果与0进行比较。
在这里插入图片描述

3 通用原理

  通常,在设计一种文件格式时,首先要确定寻址方式:是否要将文件拆分成为相同大小的页、那些页由单个块或多个连续块所组成。大多数原地更新的存储结构都是用相同大小的页,这么做有利于简化读取和写入访问。仅追加的存储结构通常页写入数据;记录被一条一条地追加上去,一旦内存中该页写满了,就将其刷到磁盘上。
  文件通常以定长的头部开始,可能以一个定长的尾部结束。尾部包含需要被快速访问的辅助信息或解析文件其余部分所必要的信息。文件的其余部分被分成多个页,下图展示了文件的大致组织方式:
在这里插入图片描述
许多数据库的表结构是固定的,其指定了表中字段的数量、顺序和类型。固定的表结构有助于减少存储的数据量:只需使用位置标识符而不用让每条记录都带上字段名。
  构建更复杂的结构常常会涉及层次结构:原始类型构成字段,一组字段构成单元,多个单元构成页,页组成段,段构成区域等等。没有必须遵守的严格规则,一切都取决于设计者为怎怎样的数据创建格式。
数据库文件通常由多个部分组成,并且在该文件的头部、尾部或者另一个单独文件中包含一个查找表,记录了这些部分的起始偏移量。

4 页结构

  数据库将数据记录存储在数据文件和索引文件中。这些文件被划分为固定大小的单元,成为页。页的大小通常是文件系统块的整数倍,一般为4~16KB。
让我们看一个磁盘上B树节点的例子。从数据结构的角度看,在B树中,我们区分叶节点(包含键和数据记录对)与非叶节点(包含键和指向其他节点的指针)。每个B树节点占据一个页或多个链接在一起的页,因此在讨论B树时,“节点”和“页”这几个术语可以互换使用。
下图为定长记录的页组织方式图,每个页是有一系列三元组组成,k表示键,v表示关联值,p表示指向子页的指针。
在这里插入图片描述
这个方案很容易实现,但是有一些缺点:
1)除非是在最右侧,否则在其他位置插入数据都会移动已有的部分数据;
2)无法有效地管理或访问变长记录,只适用于定长的数据。

5 分槽页

  当存储变长记录时,主要问题是如何管理可用空间:已删除记录所占用的空间需要被回收。如果我们把大小为n的记录放进大小为m的记录先前所占用的空间,除非m = n或者剩余空间能恰好被一个 m-n大小的记录填充,否则会出现空余空间。类似地,一个大小为m的段无法放大小为K(K > m)的记录,那这条记录会被插入其他位置,而不会回收这未使用的空间。
  为了简化变长记录的空间管理问题,我们可以将页分成固定大小的段。但在某些情况下还是会浪费一定的空间。例如我们使用64字节的段,那么除非记录长度恰好是64字节的整数倍,否则终将浪费64 - (n % 64)个字节,换句话说,除非记录记录长度是64的整数倍,否则总有一个块仅部分填充。
  空间的回收可以通过简单地重写页并移动记录来完成,但是需要保证记录的偏移量不变,因为页外指针会用到这些偏移量。在做到这一点的同时,我们希望尽可能减小空间的浪费。

为此,迫切需要一种页格式,允许我们:
1)以最小的开销存储变长记录;
2)回收已删除记录占用的空间;
3)引用页中的记录,无论这些记录具体在什么位置。

为高效地存储变长记录,例如字符串、二进制大对象等,我们可以采用一种分页槽的技术,即将页分成若干的槽。这一技术被广泛地应用在许多数据库,如PostgreSQL。
  我们将页组织成一个槽或者单元格的集合。并将指针和单元格分别存放在页的两侧。若想保持记录原有的顺序,我们只需要重新组织指向单元格 的指针。若要删除一条记录,我们只需将记录的指针为空或删除指针即可。
  分槽页具有一个固定大小的页头,包含相关信息:页大小,checksum,页的可用偏移量等信息。而单元格的大小不是固定的,可以容纳任意的数据:键、指针和数据记录等。下图展示了典型的分槽页组织方式,其中每个页都维护了一个区域、单元格和指向它们的指针。
在这里插入图片描述
分槽页的好处:
1)最小开销:分页槽唯一的额外开销是指针数组,它用于记录对应数据在页内的偏移量。
2)空间回收:通过对页进行碎片整理和重写,便可回收空间。
3)动态布局;从页外部,只能通过槽ID来引用槽,具体位置由页内指针决定。

6 单元格布局

  有了标志位、枚举值和原始类型,我们就可以开始设计单元格布局了。之后将单元格组合页,再将页组合树。单元格分为键单元格和键值单元格两种。键单元格包含一个分割键和一个指针,该指针向两个相邻键之间的页。键值单元格包含键和相关联的数据记录。

  我们假定单个页面所有单元格都是统一的(例如,要么全是键单元格,要么全是键值单元格;类似地,要么全都包含定长数据,要么全是包含变长数据,但不能是两者的混合)。这样一来,单元格的元数据只要每个页上保存一份即可,而不用让每个单元格保存一份。

构成一个键单元格需要以下信息:
1)单元格类型
2)键的长度
3)该单元格指向的子页ID
4)键的数据
一个变长键的单元格布局类似如下:
在这里插入图片描述
  我们将定长的字段放在一起,之后是key_size个字节,这有利于简化偏移量的计算,因为所有定长字段都可以通过静态的、预先计算好的偏移量来访问,我们只需为变长数据计算偏移量。

键值单元格存放数据记录而非子页ID。除非以外,它和键单元格的结构类似:
1)单元格类型
2)键的长度
3)值的长度
4)键的数据(以字节表示)
5)数据记录的数据(以字节表示)
在这里插入图片描述
由于页大小是固定的,并且页由页缓存来管理,我们只需要存储页ID即可,使用时在通过查找表将其转换成真正的文件偏移量。单元格偏移量是页局部的概念,它是相对于起始页位置的偏移量,因此可以使用较小的整数基数,使格式更紧凑。

7 将单元格放进分槽页

  要把单元组织成页,按照3.5讨论过的技术。我们将单元格追加至页的右侧,单元格偏移量/指针放置在页的左侧,如图所示:
在这里插入图片描述
  键可以不按顺序插入,其逻辑上的顺序是通过维护偏移量指针的顺序来实现的。这总设计使得向页中追加单元格只需要最低限度的工作量。因为无论是在插入、更新还是删除操作中,始终都不需要移动单元格的位置。

参考:数据库系统内幕,机械工业出版社

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

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

相关文章

unicms 使用thinkphp8 重构版

unicms 有你存在 一切安好 2023年6月使用thinkphp8,重构了cms。php最低使用php8版。参考了多个cms结合10年的开发经验,打造了开箱机用,简单的cms系统。当然由于你能力和开发时间限制,肯定有bug。欢迎大家指正。我也会不定期的修改…

【线性规划模型】

线性规划模型:原理介绍和预测应用 引言 线性规划是运筹学中一种重要的数学优化方法,被广泛应用于各个领域,包括工业、经济、物流等。 线性规划模型的原理 线性规划模型的目标是在一组线性约束条件下,寻找一组变量的最优解&…

Emm_V4.2步进闭环驱动器说明书Rev1.1

一、产品介绍 1.1 产品简介: Emm42_V4.x步进闭环驱动器是张大头智控为满足广大用户需求而自主研发的一款稳定可靠的产品,它是基于上一代Emm42_V3.6版本升级而来,不仅延续了其优秀的FOC矢量闭环控制算法,更在其传统的Dir/Step控制模…

Java003——编写和运行第一个Java程序HelloWorld

一、使用记事本创建Java并运行 1.1、设置文件显示后缀名 目的是为了方便查看文件类型 1.2、创建一个HelloWorld.java文件 java程序文件都是以.java后缀结尾的 1.3、编写Java程序 编写一下程序,并保存 public class HelloWorld {public static void main(Strin…

100天精通Golang(基础入门篇)——第0天: 安装 Go 语言开发环境的基础教程,带你学习Golang之Hello Go !

文章目录 1. 下载 Go 语言的安装包:1. 安装 Go 编译器:- 双击 安装下一步:切换安装路径:下一步:安装等待安装完成安装完成 - 2. 测试安装是否成功:1. 开始学习 Go 语言:1. 创建项目2. 创建 类文件3. 输入类名4. 键入 下方代码块5.…

【C语言】让你不再害怕“指针”【c】【知识点整理】

目录 一.什么是指针&&为什么需要指针? 1.什么是指针? 2.为什么需要指针? 以一个代码为例观察地址:(这里我们可以通过调试和打印两种方式观察) 1.调试观察: 2.打印观察(…

Vue.js 中的 v-if 和 v-show 有什么区别?

Vue.js 中的 v-if 和 v-show 有什么区别? 在 Vue.js 中,v-if 和 v-show 都是用来控制元素的显示和隐藏的指令。但是,它们之间有一些区别。本文将深入探讨 v-if 和 v-show 的区别,并给出一些相关的代码示例。 v-if v-if 是一种条…

python数据分析模块

python数据分析模块 Python与数据分析的关系数据分析常用模块Python数据分析常用类库1.数组计算的数学模块-Numpy2.Pandas——数据分析核心库3.Matplotlib——绘制数据图表的 Python 库4.批量处理Excel文件的模块-xlwings5&#x…

浮点型在内存中的存储,与整型存储方式竟然不同

对于整形的存储方式&#xff0c;大家可以看我的上一篇文章&#xff0c;这篇文章来介绍浮点型在内存中存储方式。 目录 1. 问题 2. 浮点型家族&#xff1a; 3. 存储方式: 4. 读取方式&#xff1a; 5. 解题 1. 问题 我们先来看一段代码&#xff1a; #include <stdio.h&…

人体姿态估计

预测人体关键点有什么作用&#xff1f; 还原人体姿态&#xff0c;输出脸部、手部的关键点坐标3D空间内&#xff0c;关键点变为3维坐标&#xff0c;可以在三维空间中还原人体姿态&#xff0c;可以实现一键换装恢复3D的人体模型实现下游任务–行为理解&#xff0c;根据姿态判断人…

rocketmq-client-go注册消费者组的问题

一、前言 test环境服务启动&#xff0c;通过代码新注册一个customer group进行消费&#xff0c;服务一直报错如下&#xff1a; levelerror msg"fetch offset of mq from broker error" MessageQueue"MessageQueue [topicxxx, brokerNamebroker-a, queueId1]&q…

字节跳动面试挂在2面,复盘后,决定二战.....

先说下我基本情况&#xff0c;本科不是计算机专业&#xff0c;现在是学通信&#xff0c;然后做图像处理&#xff0c;可能面试官看我不是科班出身没有问太多计算机相关的问题&#xff0c;因为第一次找工作&#xff0c;字节的游戏专场又是最早开始的&#xff0c;就投递了&#xf…

人体关键点检测

title: 人体关键点检测 date: 2023-06-02 21:28:46 tags: [MMPose,cv] 人体关键点检测 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9x4nhwLf-1685714024668)(https://fastly.jsdelivr.net/gh/weijia99/blog_imagemain/1685712470039%E4%BA%BA%E4…

【stm32开发】stm32+oled最小系统板资料(原理图、PCB、示例代码)【六一】

&#x1f389;欢迎来到stm32专栏~stm32oled最小系统板 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;stm32专栏 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能…

深度学习训练营之J5周DenseNet+SE-Net实战

深度学习训练营之J5周DenseNetSE-Net实战 原文链接方法介绍SE模块应用分析SE模块的效果对比SE模块代码实现SE模块在DenseNet当中的应用参考内容 原文链接 &#x1f4cc;第J5周&#xff1a;DenseNetSE-Net实战&#x1f4cc; &#x1f368; 本文为&#x1f517;365天深度学习训练…

UART帧格式介绍

UART及通信方式简介 UART Universal Asynchronous Receiver Transmitter 即通用异步收发器&#xff0c;是一种通用的串行、异步通信总线 &#xff0c;该总线有两条数据线&#xff0c;可以实现全双工的发送和接收&#xff0c;在嵌入式系统中常用于主机与辅助设备之间的通信…

Web3 游戏团队如何在项目发布前奠定成功基础——以真实用户支持为核心的运营策略

作者&#xff1a;lesleyfootprint.network 运营 Web3 游戏项目是一项令人兴奋且具有挑战性的任务。无论是对于 NFT 游戏还是链上多人游戏&#xff0c;建立强大且高度参与的用户群体都是游戏成功的关键因素之一。 在本文中&#xff0c;我们将探讨游戏中真实参与和机器人刷量之…

class文件中,常量池之后的相关数据解析!【class二进制文件分析】

前言&#xff1a;前段时间读《深入java虚拟机》介绍到class文件的时候&#xff0c;由于理论知识较多&#xff0c;人总感觉疲惫不堪&#xff0c;就泛泛阅读了一下。在工作中使用起来知识点知道&#xff0c;但是总是需要查阅各种资料。今天有时间&#xff0c;继续整理常量池后面的…

msvcp140.dll丢失的4个解决方法,msvcp140.dll丢失的常见原因

msvcp140.dll是Windows操作系统中的一个动态链接库文件&#xff0c;由Microsoft Visual C程序库所提供。它包含了许多C函数和类的定义&#xff0c;可以为应用程序提供一些基本服务&#xff0c;比如内存管理、文件输入/输出和网络连接等功能。我们在打开游戏或者软件的时候&…

Goby 漏洞更新 |海康威视部分iVMS系统存在文件上传漏洞

漏洞名称&#xff1a;海康威视部分iVMS系统存在文件上传漏洞 English Name&#xff1a;Some Hikvision iVMS file upload vulnerabilitie CVSS core: 9.8 影响资产数&#xff1a;15294 漏洞描述&#xff1a; 海康威视-iVMS综合安防管理平台是一套“集成化”、“数字化”、…