数据库 CMU15-455 学习笔记一 (Storage)

298 阅读13分钟

最近在看CMU15-455这门课来学习数据库,顺便做一些笔记,以帮助自己更好的理解。

课程大纲

image.png

这个课程将讲述一个数据库端到端完整实现链路,包括以下几个模块

  1. Query Planning(查询计划)
  2. Operator Executor(算子执行)
  3. Access Methods(访问方法)
  4. Buffer Pool Manager(缓存池管理)
  5. Disk Manager(磁盘管理)

课程会从下到上依次介绍各个模块的实现,本篇介绍的是Disk Manager

Storage Hierarchy

image.png

数据库存储层次(Database Storage Hierarchy)是指数据库系统中数据存储的多层次结构。每个层次都有不同的存储介质和访问速度,用于存储和管理不同类型的数据。如上图所示,越往上,大小越小,成本越昂贵但是访问速度越快,越往下,大小越大,成本越低但是访问速度更慢。从上到下依次是:

  1. CPU 寄存器
  2. CPU 缓存(L1,L2,L3)
  3. DRAM(Dynamic Random Access Memory 动态随机存取存储器)
  4. SSD(Solid State Disk,固态硬盘)
  5. HDD(Hard Disk Drive,硬盘驱动器)
  6. Network Storage(网络存储):像是 AWS 的 EBS 以及 S3 这种网络存储服务。

可以将整个架构分为两类

  1. Volatile(易失性存储):
    Volatile 存储是指在断电或重启系统时会丢失存储数据的存储介质。主存储(RAM)是最常见的易失性存储类型。主存储的数据是临时存储的,供计算机系统实时使用。当计算机断电或重新启动时,主存储中的数据将被清除。Volatile 存储通常具有更快的读写速度,但不适合长期存储和持久性数据。
  2. Non-volatile(非易失性存储):
    Non-volatile 存储是指在断电或重启系统时能够保留存储数据的存储介质。硬盘驱动器(HDD)、固态硬盘(SSD)和闪存驱动器(Flash Drive)是常见的非易失性存储类型。这些存储介质可以长期保存数据,即使在断电或重启系统后仍然能够保持数据的完整性和可访问性。Non-volatile 存储适用于持久性数据存储,如操作系统、应用程序、数据库和用户文件等。

不同层级的访问速度对比如下:

image.png

Page是啥

数据库中的存储管理器(Storage Manager)是数据库管理系统(DBMS)的组成部分,负责管理数据在物理存储介质(如硬盘、固态驱动器等)上的存储和访问。

在数据库中,存储管理器的基本单位是页面(Page)。页面是数据库存储管理的最小单位。 存储管理器通过有效地管理页面的分配、释放、读写和缓存,实现数据在磁盘和内存之间的高效交互,从而提供高性能和可靠的数据存储和访问。

Page主要具有以下特性:

  1. 固定大小:每个页面具有固定的大小,常见的页面大小为4KB或8KB。这种固定大小的设计使得数据库系统能够以块的方式进行数据的存储和管理。
  2. 数据存储:页面用于存储数据记录、索引项、元数据和其他数据库对象。不同类型的页面可能有不同的结构和用途,例如数据页面、索引页面、目录页面等。
  3. 磁盘存储:页面通常存储在磁盘上,作为数据文件的一部分。它们根据需要从磁盘读取到内存中进行访问和操作,并可能在修改后写回磁盘。
  4. 缓存管理:数据库系统通常使用缓冲池(Buffer Pool)来管理内存中的页面缓存。缓冲池是一块内存区域,用于缓存最近使用的页面,以提高数据的读取和写入性能。
  5. 访问控制:页面级别的访问控制是数据库系统中的重要组成部分。系统可以使用锁机制或多版本并发控制(MVCC)等技术来确保并发事务对页面的访问互不干扰。

Page Header

每个Page都包含一个Page Header, 主要包含Page内容的元数据信息

以下是Page Header 的一些常见信息和功能:

  1. 页面标识符:页面头通常包含一个唯一的标识符,用于标识该页在数据库中的位置和身份。这个标识符可以是页号、物理地址或其他唯一标识符。
  2. 状态信息:页面头中会记录页面的状态信息,例如是否空闲、是否已分配、是否被修改等。这些状态信息有助于数据库管理系统(DBMS)对数据页进行管理和维护。
  3. 大小和布局信息:页面头包含有关数据页大小和布局的信息。这些信息可以告诉DBMS如何解释和访问该页的内容,包括页内记录的存储方式和组织结构。
  4. 时间戳和版本信息:某些数据库系统会在页面头中存储时间戳或版本信息,用于并发控制和事务管理。这些信息可以帮助DBMS跟踪和管理并发访问和修改数据页的操作。
  5. 检查点信息:页面头可能包含有关检查点(checkpoint)的信息,用于恢复和数据一致性的目的。这些信息可以记录页面的变更序列号、日志序列号等,以确保数据页的一致性和持久性。
  6. 其他元数据信息:页面头还可以包含其他元数据信息,如页内记录的数量、索引信息、页级别的统计信息等。这些信息可以用于优化查询和访问数据页。

Page Layout

关于如何管理一个Page中的数据,有以下三种方式:

  1. Tuple-Oriented storage
  2. Log-Structured Storage
  3. Index-Organized Storage

Tuple-Oriented storage

Tuple-oriented storage(元组导向存储)是一种数据库存储模型,它将数据以元组(tuple)为单位进行存储和管理。在元组导向存储中,数据库中的每个记录被视为一个包含多个属性(字段)的元组。

元组导向存储相对于其他存储模型(如基于列的存储或基于行的存储)具有一些特点和优势:

  1. 数据的完整性:元组导向存储将每个记录作为一个完整的元组存储,确保了记录的完整性。这意味着在插入或更新记录时,所有属性的值都将被写入或更新,保持数据的一致性。
  2. 数据的可扩展性:元组导向存储适用于具有动态模式(schema)的数据,因为每个记录可以具有不同的属性集合。这使得数据库具有更好的可扩展性,可以适应不断变化的数据要求。
  3. 访问效率:元组导向存储通常以记录为单位进行读取和写入,这在访问单个记录时可以提供较好的性能。此外,如果查询只需要检索特定属性的值,元组导向存储可以通过跳过不相关的属性来减少数据的传输量,从而提高查询性能。
  4. 应用领域的适用性:元组导向存储在一些应用领域具有优势,例如面向对象的数据库系统(OODBMS)和文档存储(如JSON、XML等)。这些应用通常需要处理复杂的数据结构和嵌套数据,而元组导向存储可以更好地支持这种类型的数据模型。

Slot

元组导向存储中的槽(slot)是指存储单个元组(tuple)的位置或空间。槽可以被看作是存储元组的容器,每个槽可以容纳一个元组。

关于Slot的一些重要概念和特点:

  1. 存储空间:每个槽都分配了一定的存储空间来存储一个元组。存储空间的大小取决于数据库管理系统的实现和配置,可以是固定长度或可变长度。
  2. 记录插入和删除:当插入一个新元组时,DBMS会分配一个空闲槽来存储该元组,并将元组的属性值写入槽中。类似地,当删除一个元组时,DBMS会将相应的槽标记为空闲,以便在以后的插入操作中重复使用。
  3. 槽的标识:槽通常会使用唯一的标识符来标识,例如槽号、物理地址或指针等。这样可以方便地对槽进行查找、访问和管理。
  4. 空槽管理:DBMS需要有效地管理空闲槽,以便在插入新元组时能够快速地找到可用的槽。通常,DBMS会使用一种数据结构(如位图、链表或自由空间列表)来跟踪和管理空闲槽。
  5. 槽的访问:对于元组导向存储,数据的访问是通过直接访问槽来实现的。可以使用槽的标识符来定位和读取特定的元组。通常,DBMS会维护一个元组目录或索引结构,用于记录槽与元组之间的映射关系,以支持快速的查询和访问。

Page组成

image.png

Page可分为以下四部分:

  1. Header:页的头信息,包含一些元信息MetaData,例如当前Page存储Record的数量,总空间大小,剩余空间大小等
  2. Slots:记录每个Record在本页中的offset,向后增长
  3. Records:实际每条Record存储的位置,向前增长
  4. Free Space:空闲空间

展示

课堂上老师通过展示SQL Server和Oracle等数据库来直观的感受数据在Slot上的存储

image.png

缺点

Tuple-Oriented storage 存在以下几个缺点

  1. Fragmentation: Pages没有被充分完全利用, 存在unusable space和empty slot的场景
  2. Useless Disk I/O: DBMS为了更新一个Tuple需要fetch整个Page
  3. Random Disk I/O: 在同时更新多个Tuple,且都属于不同的Page的场景下,随机读效率会很低

Log-Structured Storage

Log-Structured Storage (LSS)使用日志结构来组织和存储数据。基本想法是将数据以日志形式追加写入,而不是就地覆盖现有数据。具体而言,数据库中的每个操作(例如插入、更新或删除记录)都被写入一个顺序写的日志文件中,称为 "日志" 或 "写前日志"(write-ahead log,WAL)。这些日志记录按照时间顺序追加到日志文件的末尾。

随着数据的不断追加写入,数据库系统在内存中维护一个数据结构,通常称为 "索引" 或 "映射表",用于将逻辑地址或键映射到日志文件中的物理位置。通过索引,可以快速查找和检索基于键或地址的数据。

image.png

特点

Log-Structured Storage常用压缩和排序来对日志文件进行优化

  1. 压缩: 在LSS中,随着时间的推移,日志文件可能会越来越大,其中可能包含重复、过时或不再需要的数据。为了回收空间并提高存储效率,可以定期对日志文件进行压缩操作。压缩的目标是消除重复的记录、删除过时的数据,并重新组织日志文件以减少存储空间的占用。压缩操作通常在后台进行,可以使用各种算法和技术,例如基于时间戳的过期数据删除、重复记录的合并、数据块的重新组织等。
  2. 排序: 在LSS中,数据是按照时间顺序追加到日志文件中的。然而,为了提高读取性能,可以对日志文件进行排序操作。排序的目的是将数据重新组织,使得相关数据更加紧凑地存储在一起,减少随机访问的开销。排序操作可以按照不同的维度进行,例如按照键值排序、按照时间戳排序等。排序操作通常在后台进行,并且可以根据需要定期触发或在特定条件下触发。

压缩和排序操作可以结合使用,以进一步优化LSS的性能和存储效率。它们可以减少存储空间的占用,提高读取性能,并且在数据访问时可以减少随机I/O的需求。然而,这些操作也会引入一定的开销,包括计算资源和时间成本。因此,在选择和配置数据库系统时,需要权衡压缩和排序操作的效益与开销,并根据具体的应用场景进行调整和优化。

image.png

Index-Organized Storage

在传统的数据库存储中,数据通常以行的形式存储在数据页中,而索引以独立的结构存储。而在索引组织存储中,数据行按照索引的结构组织,即数据行的物理存储顺序与索引的逻辑顺序一致。这样,当执行基于索引的查询时,可以通过索引的结构迅速定位到所需的数据行,减少磁盘I/O的开销,从而提高查询性能。

image.png

Tuple Layout

Tuple Header

每个Tuple都有一个Header, 是存储在数据库表中每个元组(行)前面的一部分数据。元组头包含了关于该元组的元数据信息,用于描述和管理元组的结构和属性。

元组头通常包含以下信息:

  1. 元组长度(Tuple Length): 元组头存储了该元组的长度,即元组占用的字节数。这个信息可以帮助数据库系统在查询和存储时快速定位和操作元组。
  2. 空位图(Null Bitmap): 如果元组中的某些属性值为NULL(空值),则在元组头中会有一个空位图,用于记录哪些属性是空的。空位图是一个位图,其中的每个位对应一个属性,如果位为1,则表示该属性为NULL;如果位为0,则表示该属性有非空值。
  3. 可见性信息(Visibility Info): 是用于管理并发控制和事务隔离的一组信息。它用于跟踪和确定哪些事务可以看到或修改数据库中的特定元组。 image.png

Tuple Data

大部分系统会按照建表时的属性顺序去存放数据,然而有的系统会对他们进行排序以提高访问效率。

image.png