CMU DBMS Storage Part1

Storage

我们将关注面向磁盘的数据库管理系统架构,该架构假定数据库主要存储位置是在非易失性磁盘上。

在存储层次结构的顶部,有最接近cpu的设备。这是最快的存储设备,但也是最小和最贵的。离cpu越远,存储设备的容量就越大,价格越便宜,但速度也会慢很多。

易失性存储:

  • 易失性存储的意思是机器断电之后,数据就丢失了。
  • 易失性存储支持字节可寻址位置的快速随机访问。means 程序可以跳转到任何字节地址,并获取数据。
  • 我们将此类存储称为“内存”。

非易失性存储:

  • 非易失性存储意味着不需要保持电源来保存它存储的数据
  • 它是块/页可寻址的,这意味着,为了读取特定偏移量的值,程序首先必须将4kb页面加载到内存中,该内存保存程序想要保存的值。
  • 传统磁盘非易失性存储更适合顺序访问(同时读取多个数据块)。
  • 我们将其称为磁盘,不区分ssd和hdd。

由于数据库系统假设数据存储在磁盘上,所以DBMS的职责就是如何从内存和磁盘中来回移动数据,因为系统不能直接在磁盘上对数据进行操作。

我们将重点关注减少磁盘和内存之间的IO延迟,而不是寄存器和缓存的优化,因为前者性价比更高。

面向磁盘的数据库管理系统概述

数据库全部在磁盘上,数据库文件中的数据被整理成页,第一页为目录页。为了对数据进行操作,DBMS需要将磁盘上的数据读取到内存,它通过一个缓冲池来管理磁盘和内存之间的来回移动来实现。DBMS还有一个执行引擎来执行查询,执行引擎向缓冲池请求特定的页面,缓冲池负责将该页面读入内存,并给执行引擎一个指向内存中的页面的指针。

DBMS vs. OS

DBMS的一个高级目标是实现支持超过可用内存数量的数据库。由于磁盘的读写是非常昂贵的,所以必须小心管理。我们不希望从磁盘上取数据时出现大的延迟,从而减慢其他所有操作的速度。因此我们希望DBMS能够在等待从磁盘获取数据时处理其他查询。

这个高级设计目标类似与虚拟内存,有一个大的地址空间用来供操作系统从磁盘读取pages到内存。

实现虚拟内存的一种方法是使用mmap在进程地址空间中映射文件的内容,这让操作系统负责在磁盘和内存之间来回移动页面。不幸的是,这意味着如果mmap遇到页面异常将阻塞这个进程。

  • 你永远不会想使用mmap在你的DBMS中如果你需要写入数据的时候。(多个并发写 需要同步机制)
  • DBMS机会总是想自己控制这个,而且能更好地完成,因为它对正在访问的数据和正在处理的查询了解得更多。
    • 按正确的顺序将脏页刷新到磁盘
    • 特殊的“预取”
    • 缓冲区替换策略
    • 线程/进程 调度
  • 操作系统不是你的朋友。

可以通过操作系统做这些事情

  • madvise:告诉操作系统你什么时候打算读取某些页面。
  • mlock:告诉操作系统不要把内存交换到磁盘。
  • msync:告诉操作系统把内存刷到磁盘

出于正确性和性能的原因,我们不建议在DBMS中使用mmap。尽管操作系统可以提供这些功能,但让DBMS自身实现这些过程会使其具有更好的控制和性能。

File Storage

DBMS根本上还是将数据以文件的形式存储到磁盘上。有些可能使用文件层次结构,有些使用一个单文件(SQLite)。

操作系统不知道这些文件的内容是什么,只有DBMS知道怎么去解释它的内容,因为这些文件格式都是专属与某个DBMS的。

DBMS的存储管理器负责管理数据库文件。它将文件表示为页面的集合。它还可以跟踪哪些数据已读到页面,以及页面中有多少可用空间。

Database Pages

DBMS将数据库组织在一个或多个文件中,这些文件由称为页的固定大小数据块组成。页面可以包含不同类型的数据(元组,索引)等。大多数系统不会在页面中混合这些信息。有些系统要求它是自包含的,这意味着读取每个页所需的所有信息都在页面本身。

每个页面都有一个唯一的标识符。如果数据库是一个单独的文件,那么page id可以只是文件偏移量。大多数DBMS都有一个中间层,它将page id映射到文件路径和偏移量。系统上层将要求指定页码,然后存储管理器必须将该页码转换为文件和偏移量才能找到该页。

大多数DBMS使用固定大小的page来避免支持可变大小page所需的工程开销。例如,对于可变大小的page,删除page可能会在文件中形成一个空隙,DBMS无法轻松地用新页面填充这些空隙。

DBMS有三个page的概念:

  1. 硬件 page (通常是4kb)
  2. 操作系统 page (4kb)
  3. database page (1-16kb)

存储设备保证对硬件页大小进行原子写入。如果硬件页是4Kb,那么当系统尝试向磁盘写入4kb时,要么4kb全部写入,要么全部不写入。这意味着,如果我们的数据库页面大于硬件页,DBMS将不得不采取额外的措施,以确保数据被安全地写出来,因为当系统崩溃时,程序可以通过将数据库页面写入磁盘来完成一部分工作。

Database Heap

有几种方法 数据库可以找到page在磁盘上的位置,堆文件组织方式是其中一种。

堆文件是按随机顺序存储元组的无序页集合。(注:关系模型并没有任何排序,如果一个接一个插入tuple,并不能保证它们是按照插入顺序保存在磁盘上的。)DBMS可以通过使用page的链表或page目录在给定page id的磁盘上定位页面。

  1. Linked List:头页包含一个指向可用page列表和指向数据page列表的指针。然而,如果DBMS正在寻找一个特定的page,它必须对数据page列表进行顺序扫描,直到找到它要寻找到的page为止。
  2. Page Directory: DBMS维护跟踪数据page的位置以及每个page上可用空间量。

Page Layout

每个页面都包含一个头部,用于记录有关页面内容的元数据:

  • Page size.
  • Checksum.
  • DBMS version.
  • Transaction visibility. 事务可见性
  • 有些系统要求page是自包含的。(例如oracle)

布局数据的一种方法是跟踪DBMS在一个页面中存储了多少元组,然后每次添加一个新元组时,它都将元组追加到末尾。但是,当删除元组或元组具有可变长度属性时,会出现问题。

在page中布局数据有两种主要方法:(1)slotted-pages (2)log-structured.

Slotted Pages: Page提供slots和偏移量的映射

  • 当今DBMS最常用的方法

  • Header跟踪使用的插槽数和上次使用的插槽起始位置和插槽数组(插槽数组跟踪每个元组的起始位置)

  • 要添加元组,插槽数据将从头到尾增加,元组将从尾到头增加。当槽数组和元组数据相遇时,就认为page满了

Log-Structured: DBMS只存储日志记录而不是存储元组

  • 存储数据库怎么被修改的记录到文件(添加,修改,删除)
  • DBMS倒着扫描日志文件并“重新创建”元组去读取一条记录
  • 写得快,读的慢
  • 在只是append的存储场景下工作的好,因为DBMS无法go back并更新数据。
  • 为了避免长时间的读取,DBMS可以使用索引来允许它跳转到日志中指定位置。它还可以周期性的压缩日志(如果它有一个元组,然后对其进行了更新,那么它可以将其压缩为只插入更新的元组)。压缩的问题是DBMS最终会导致写放大。(它会一遍又一遍地写入相同的数据)。

Tuple Layout

元组本质上是一个字节序列。DBMS的工作是将这些字节解释为属性类型和值。

Tuple Header:包含关于元组的元数据

  • DBMS并发控制协议的可见性信息(即有关创建 修改该元组的事务的信息)
  • 空值的位图
  • 注意,DBMS不需要在这里存储关于数据库模式的元数据

Tuple Data:属性的实际数据

  • 属性通常按照创建表时指定的顺序存储
  • 大多数DBMS不允许元组超过页面大小

Unique Identifier:

  • 数据库的每一个元组都分配有唯一的id
  • 大部分都有: page_id +(offset or slot)
  • 应用程序不能依赖这些id来表示任何东西

Denormalized Tuple Data:

如果两个表是相关联的,DBMS可以“预连接”它们,因此这些表最终在同一页上,这使得读取速度更快,因为DBMS只需在一个页面中加载,而不必在两个单独的页中加载,但这使得更新更为昂贵,因为DBMS需要为每个元组提供更多的空间。

posted @ 2020-06-05 17:12  liuliushun  阅读(41)  评论(0)    收藏  举报