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的概念:
- 硬件 page (通常是4kb)
- 操作系统 page (4kb)
- database page (1-16kb)
存储设备保证对硬件页大小进行原子写入。如果硬件页是4Kb,那么当系统尝试向磁盘写入4kb时,要么4kb全部写入,要么全部不写入。这意味着,如果我们的数据库页面大于硬件页,DBMS将不得不采取额外的措施,以确保数据被安全地写出来,因为当系统崩溃时,程序可以通过将数据库页面写入磁盘来完成一部分工作。
Database Heap
有几种方法 数据库可以找到page在磁盘上的位置,堆文件组织方式是其中一种。
堆文件是按随机顺序存储元组的无序页集合。(注:关系模型并没有任何排序,如果一个接一个插入tuple,并不能保证它们是按照插入顺序保存在磁盘上的。)DBMS可以通过使用page的链表或page目录在给定page id的磁盘上定位页面。
- Linked List:头页包含一个指向可用page列表和指向数据page列表的指针。然而,如果DBMS正在寻找一个特定的page,它必须对数据page列表进行顺序扫描,直到找到它要寻找到的page为止。
- 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需要为每个元组提供更多的空间。

浙公网安备 33010602011771号