数据库内核从入门到精通 —— 数据库存储引擎

存储器的层次

Q:存储器的分层结构是什么样的?

A:寄存器 -> 缓存(cache) -> 内存(主存储器) -> 磁盘,如下图的金字塔结构。

这些存储器从上往下的特点如下:

  • 存储容量由小到大
  • 速度由快到慢
  • 花费由高到低。

磁盘

Q:磁盘有哪些特征?

A: 磁盘由盘面、磁道、扇区组成,对数据的访问需要先寻道,然后再定位到特定扇区,最后再读取数据。

磁盘容量 = 盘面数量 * 磁道数量 * 扇区数量 * 扇区大小

Q:磁盘中读写的最小单位是什么?多少字节?

A:扇区是最小的可以从磁盘进行读写的数据单元,扇区大小通常为512字节;主存和磁盘之间通常使用块进行数据传输,一个块的大小通常为4k-16k。

磁盘快速访问

Q:数据读取时间由 寻道时间 + 旋转延迟(定位扇区)+ 传输时间组成,如何针对这三部分时间进行优化磁盘访问时间?

A:磁盘上的数据传输时间通常无法改变,磁盘上不支持地址随机访问,所以要加快磁盘上的数据读取,思考方向为减少寻道时间、旋转延迟,例如尽可能进行连续磁盘读写,减少寻道时间、扇区定位时间。

Q:都有哪些手段加速访问?

A:

  • 数据预取 + 数据缓存,利用主存的能力

  • 磁盘镜像,将数据进行复制,提高数据的读能力

磁盘故障

Q:磁盘发生故障如何解决?

A: RAID(redundant arrays of independent disks)可以用来减少磁盘崩溃时数据丢失的风险。

  • RAID1:每个数据盘对应一个镜像盘,将数据原封不动写到镜像盘做备份

  • RAID4:RAID4不管有多少个数据盘,仅使用一个冗余盘

    • 假设现在有数据盘1、2、3,它们相同位置上的数据分别为 11110000、10101010、00111000,则冗余盘4的对应数据为它们的 xor 值 01100010
    • 通过上述方式可以对数据进行校验以及故障恢复
  • RAID5:每个盘的某一块作为其他盘对应块的备份。例如盘1的第一个块作为其他盘对应位置块的备份(奇偶校验和),盘2的第二块作为其他盘的备份...,这种情况下一个盘出现了故障的话,可以通过其他盘进行恢复,同时也没有增加额外的盘。

组织磁盘上的数据

Q:数据库中的表对应的存储形式?数据在磁盘中存储组织的形式?记录的存储形式?

A:数据库由多个文件组成,每个文件包含多个块(每个块的大小4-8KB),每个块包含多条记录,数据以记录的形式被承载;记录又可以分为定长记录以及变长记录:

  • 定长记录:记录包括首部以及记录内容

    • 首部:记录元信息的指针(chema);记录长度;时间戳

      • 记录的元信息格式:字段的数量;每个字段的类型;字段在记录中的顺序;每个字段的名称
      • 通过元信息的指针可以找到记录的 schema 对读取到的记录 buffer 进行解析;通过记录长度可以知道要读取的buffer的尾部位置

  • 记录在块中存储的方式,记录的元信息(记录的首地址,记录的长度)从低地址往后增长,记录本身从块的尾部向前存放,使用一种叫做 slotted-page structure的方式在块中组织记录,块头主要包含以下信息:

    • 记录数
    • 空闲空间指针
    • 记录数组entries(记录偏移,记录长度)
    •   记录从块的尾部往前放,在header aray的尾部和第一个记录首部之间的空间称为 free space(空闲空间),用来存放新插入的记录以及新的entry。

变长的数据和记录

对于变长记录来说,如何在块中存储变长记录以及变长字段方便快速提取

Q:具有变长字段的记录以什么格式存储?

A:记录的存储格式为 <首部信息(schema/时间戳),记录长度,变长字段的指针,变长字段的长度, ... ,非变长字段数据内容,... ,变成字段内容 ... >。其中第一个非变长字段的指针无需存储,可以通过 schema 的信息计算偏移得到。 变长字段通过 <offset, length> 进行标识

Q:可变格式的记录以什么格式存储?

A:可变格式的记录中,一个块中存储的记录可能存在不同的 schema,因此需要将每个记录的字段元信息和记录本身存储在一起,包括字段数据类型、字段长度

Q:不能装入一个块中的记录以什么格式存储?

A:在每个记录的起始位置处记录一个指针,指向它的上一个/下一个块的记录部分。

块和记录地址的表示

Q:记录存储在磁盘中,访问每条记录的地址形式的什么样的?

A:拿磁盘索引B+ 树举例,索引中的每个节点通常是以page为单位在磁盘中进行存储。当从磁盘中读取一个节点后,需要根据当前节点读取它的孩子节点,可以通过记录 pageId 的形式将每个孩子节点的地址存储在父节点中。

指针混写

数据块从磁盘读取到内存前,数据块中需要保存每个孩子节点的 pageId;当数据块从磁盘读取到内存后,可以使用虚拟地址进行访问,减少查页表时对pageId进行地址转换

  • 目的:避免将数据库地址(pageId)重复转换成内存地址的开销

  • 混写的思想:当将数据块从磁盘读取到主存时,块内指针可以混写,即从数据库地址空间转换为虚拟地址空间

  • 指针混写内容:一个二进制位,指明指针目前是数据库地址还是内存地址

  • 混写策略:

    • 自动混写
    • 按需混写:根据数据块的使用频率来确定是否需要混写
    • 不混写
  • 数据块写回磁盘:当块被从主存移回到磁盘中时,必须接触块内的混写

  • 被钉住的记录和块:可以继续使用磁盘索引 B+ 树的例子进行理解,当新增了一个叶子节点后,叶子节点刷入磁盘后,才能知道它的数据库地址(pageId),在这之后它的父节点的孩子节点指针才能被正确更新并写到磁盘,否则父节点都不能被写磁盘,因为它的孩子节点地址是无效的。

举例说明

以 CMU15445 中 B+ Tree project 为例,说明不使用指针混写的情况下,是如何通过 pageId 来找到子节点,主要包括以下两步:

  1. 可以看到调用 GetValue 这个接口通过索引找指定 Key 对应的所有 Value,首先拿到根节点,然后通过根节点去找子节点,找子节点的过程就是红线标出来的,先拿到子节点的 pageId,然后在 bufferPool 中找是否子节点是否已经加载到内存

  1. 接下来看 FetchPageImpl 这个接口,在 bufferPoolManager 中维护一个映射,将pageId映射到内存地址,所以每次查询 pageId 对应的子节点时,都需要通过查一遍映射表做一层转换。

记录的修改

Q:修改如何写入磁盘?(新增、修改、删除)

A:

  • 插入:

    • 记录没有特性的顺序:将新纪录插入到块的空闲位置或是已删除记录的地方
    • 记录有特定的顺序:如果邻近块有空闲空间,则移动记录到邻近块即可;否则,创建一个溢出块
  • 删除:

    • 及时回收空间
    • 标记删除
  • 修改:

    • 定长记录:对存储无影响

    • 变长记录:相当于删除原纪录,插入新纪录,方法和插入与修改的方向一样

reference:

  • Database-System-Concepts
  • 阿里云数据库培训
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值