CMU-15445数据库系统导论笔记<四>—存储引擎

684 阅读11分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

引言

这一节要了解的的就是:数据库系统是如何存储它所管理的数据的

数据库是管理数据的一种组织方式,数据在计算机中有多种存储方式,分别对应不同的存储介质

上图左侧是相对完整的计算机存储介质分类,右边是它们存储效率的对比,值得一提的有三个东西,一是Network Storage并不是都那么慢,有的企业专用万兆网口网盘可以达到非常快的交互速度,甚至比固态硬盘还快。二是上面没有出现的一种存储介质是Insistence Memory即持久内存,这是还未普及的技术,基本就是将内存改造为可以关机保存数据的volatile memory,比普通的内存更快更好用。三是Tape Archives,一种基于磁带的存储介质,特点是存储空间非常的大,但是慢的要死,所以基本上作为大数据库的删库备份。

关于volatile和non-volatile的区别我直接抄note了

Volatile Devices

  • 易失性意味着如果你从机器上拔出电源,那么数据就会丢失。
  • 易失性存储支持具有字节可寻址位置的快速随机访问。这意味着程序可以跳转到任何字节地址并获取那里的数据。
  • 就我们的目的而言,我们总是将这个存储类称为“内存”。

Non-Volatile Devices

  • 非易失性意味着存储设备不需要连续的电源,以便设备保留它正在存储的比特。
  • 它也是可寻址的块/页。这意味着,为了在特定偏移量处读取值,程序首先必须将4KB页加载到内存中,该内存保存程序希望读取的值。
  • 非易失性存储通常更适合顺序访问(同时读取多个连续的数据块)。

我们要讨论的是disk-oriented DBMS,也就是基于硬盘-文件的系统,基于内存的系统也有例如redies,特点是非常的快,但是空间小,关机必须备份数据。大部分系统是disk-oriented,所以我们讨论这一种。

要想了解DBMS的存储机制,首先要知道DBMS的存储系统设计目标是什么,

  • 支持超出可用内存量的数据库,不能8G内存一次只能存8G数据
  • 由于对磁盘的读写非常昂贵,我们不希望从磁盘获取某些内容时出现大的停顿,从而降低整个系统的速度
  • 我们希望DBMS在等待从磁盘获取数据时能够处理其他查询,也就是并发处理

上图是disk-oriented DBMS的基本架构,我们会按照Disk、Memory以及其它机制的顺序解释这些都是什么,这里不过多讲解。

一个有趣的问题是,DBMS的存储系统设计与操作系统的存储设计是有些类似的,而且操作系统可以全盘接管与硬盘的交互,那么我们为什么不直接利用操作系统的机制来设计DBMS呢。例如系统mmap函数可以分配内存到进程中,而且还有madvise、mlock等函数辅助

答案是,操作系统大多处理与内存交互的逻辑,而与硬盘交互的设计性能太差了。在数据库管理上,DBMS几乎能够在任何方面胜过操作系统,包括在哪些操作系统已有的机制例如prefetching、replacement等。

关于操作系统和DBMS在管理上的关系之后还会提及

那么DBMS如何在硬盘里组织存储于文件中的数据库?可以从三个方面来说File Storage、Page Layout和Tuple Layout、Log-Structured

File Storage

database存储在database file中,一般有一个或多个database file。首先DBMS管理database file的读和写,操作系统读取database file只能得到一串乱码。同时数据在文件中是以page的形式存储的,这个page与OS和硬盘的page都不同,是专门针对DBMS的page,一个page一般大小在512B-16KB之间,并且可以更改配置文件来修改page size

如上图所示,一个Database File中含有大量的page,其中会有一些page存储元数据,即包含文件内page情况信息的数据。大多数DBMS采用的是我们熟悉的page directory来组织pages,DBMS通过元数据查找某个tuple的位置,表relation内容等等信息。

页内容和元组内容

上图是page中的结构,包含页元数据,同样含有一个Header存储了这个page相关的信息,Header中一般含有的部分我已经在图里标注了

Data中数据的存储形式一般有两种流派,分别是Tuple-oriented和Log-structured

首先介绍Tuple-oriented的方法,其中Data存储的即是表中的一行行Tuple,具体的存储结构如下图左边所示,Slot Array用于指向Tuple的位置,SlotArray和Tuple是像地址空间中的heap和stack一样向中间靠拢的。

DBMS在查询表中的某一个tuple时,会用到Record ID,即每一个Tuple独有的编码,计算方法是page_id + offset/slot,通过ID可以查询到database file中任意一个tuple。Record ID只对DBMS有意义。

而具体某一个Tuple中的结构如则右图所示,即根据relation的顺序来存储。

Log-structure

slot形式的存储如上图所示,将一条条完整的tuple连续的存储在page中,另一种形式的存储叫log-structure,也就是基于变化的存储形式。具体的说,在对数据进行操作时,基于log的方式将操作存储在page中,而不是对数据进行直接操作,而读的时候反向推导log,得到数据的具体信息,过程如下图所示。

这种方法有一个很明显的缺点,log信息冗余过多,解决办法是定时的进行压缩,压缩的基本逻辑就是将数据的基本属性分组推导存储在page中,压缩的顶层策略一般有分层压缩和全局压缩两种,具体看下图

这种存储方式一般只用于KV数据库和少量时序数据库,因为比较适合KV数据库一值一键的操作逻辑。

这种存储形式看上去并不是十分好理解,它的最大好处是可以将写操作由随机写变为顺序写,例如要更新一条数据,如果是基于关系的数据库此时就要查找到数据来源位置再进行写操作,而基于log的数据库直接在当前page有空余的地方写下log即可。

Data Representation

我们已经知道数据库存储形式的最小单位一般是tuple,那么在tuple中的数据在page中的二进制是怎么表达呢,具体来说,不同数据结构在tuple中怎么存储呢。

整型: 大多数DBMS使用IEEE-754标准所指定的“原生”C/ C++类型来存储整数。这些值是固定长度的

时间戳: 日期/时间的表示方式因系统而异。通常,它们表示为自unix时代以来的一些单位时间(微秒/毫秒)。

低精度浮点型

可变精度的浮点型使用IEEE-754标准指定的“本机”C/C++类型。这些值也是固定长度的。由于CPU可以直接在可变精度数上执行指令,因此对可变精度数的运算比对任意精度数的运算速度更快。然而,由于某些数字无法精确表示,在执行计算时可能会出现舍入错误。例如0.1+0.2和0.3在小数点后8位会出现较大差异。

高精度浮点型

对于网上交易等类型的业务是严禁使用低精度浮点型作为金额的存储类型的,在Java中有一种数据类型叫BIGDECIMAL,可以保证每一位数字都是绝对准确的,原理就是用字符串表示浮点数。数据库也用了类似的原理。例如在PostgreSQL中,有一种数据类型NUMERIC,它的源码结构体如下图所示。

当精度错误不可接受时,就可以使用这些数据类型,但相应的会增加性能开销。

超长数据

当我们保存一段很长(存储占用很大)的数据时,有可能发生page空间不够的情况。这种时候系统会分配溢出页给过大的数据,原page只存储指向溢出页的指针,这些溢出页可以包含指向其他溢出页的指针,直到可以存储所有数据为止。当然这种溢出页难以避免的会造成内部空间碎片和一系列其它问题。因此对于大字段,尽量将它存储在别的地方,tuple中只存储能够索引到这些大字段的方法。

元数据

绝大多数DBMS会在系统中创建很多元数据表,存储有关于这个系统中所有数据的总体信息,例如表、行、列、视图、用户、权限、内部统计数据等等数据。每个DBMS对这些数据的定义不同,但是内容种类大差不差。

具体的,大多数数据库都有INFORMATION_SCHEMA,SQL-92标准也定义了INFORMATION_SCHEMA的一些查看方法

Database WorkLoad

workload就是指数据库的工作负载,即数据库的日常操作是什么性质的、处理哪些数据、怎样处理等待。

OLTP & OLAP

Online Transaction Processing即OLTP指事务性的操作模式,数据库在面向用户时,所接收的大多是简单的增删查改操作,并不复杂但是频率高,并发性强,如银行服务。Online Analytical Processing即OLAP指分析性的操作模式,数据库在整合数据以进行后续的展示性分析性的工作时,所接收的大多是复杂的整表操作,实时性要求不高,但是对计算性能有一定考验,如企业大数据处理。

与这两个概念对应的是我们在配置一个完整业务数据库时要遵循OLTP与OLAP结合的设计思路,从用户侧开始,用多个小型的高并发数据库构建OLTP,这一端称为OLTP Data Silos。从用户处收集数据后,经由ETL处理成为能够用于分析的数据,存储在大型高性能数据库中,用于构建OLAP,这一端称为OLAP Data Warehouse。

另一种数据库叫做HTAP,目的是统一OLTP和OLAP,但是貌似效果不好

Storage Models

关系模型的定义并没有要求数据在page中如何存储,事实上用户并不管tuple在page中是如何存储的。上文提到OLTP和OLAP是两种性质非常不同的workload,我们可以使用不同存储模型来分别针对优化OLTP和OLAP

N-Ary Storage Model

在n-ary存储模型中,DBMS将单个元组的所有属性连续存储在单个页面中,因此NSM也被称为“行存储”。这种方法对于OLTP工作负载非常理想,在这种负载下,请求需要大量插入,TP任务往往只操作单个实体,而不需要大量访问不同的实体。

如图,在这种典型的TP操作下,NSM和操作本身十分契合,但是在比较复杂的SQL语句里比如AP操作,这种模型使得针对于某一两个属性的大量操作十分困难,因为查询到其它属性的信息是冗余的。因此NSM优缺点可总结为以下几点

优点

  • 插入、更新、删除操作快速
  • 对整个tuple的查询十分有利

缺点

  • 不适合扫描表的大部分和/或属性的子集。这是因为它会获取处理查询不需要的数据,从而污染缓冲池

Decomposition Storage Mode

在Decomposition Storage Mode中,DBMS将所有元组的单个属性(列)连续地存储在一个数据块中。因此,它也被称为“列存储”,这是一种不存储tuple之间的关系的存储方式,它的优缺点几乎是NSM的互补。

优点

  • 减少了查询执行过程中浪费的工作量,因为DBMS只读取查询所需的数据。
  • 支持更好的压缩,因为相同属性的所有值都是连续存储的。

缺点

  • 由于tuple分割/拼接,点查询、插入、更新和删除速度慢。

实现列存主要有两种方式,偏移和嵌入ID

在21世纪之前只有几个数据库是支持列存的,而现在几乎所有数据库都会多做一套列存的引擎。

Summary

数据库和数据在硬件存储中的不同表达给我们传递出了一个信息,DBMS和数据的存储并不是割裂开的。不同数据库之间的存储引擎并不通用,各自都是在对自己最有利的方向上优化。

\