Loading

数据库存储与文件结构

文件组织

一个数据库会被映射到多个不同的文件,存储在磁盘上,这些文件被分成特定长的存储单元,称为,大部分数据库默认情况下的块大小是4~8KB,大部分情况下这个值可以修改。

一个块中可能包含很多条记录,并且保证没有一条记录横跨多个块,这也限制了不可以有一条比块更大的记录,大部分需求中,字段只包含一些简单的数字和文字,确实不太可能比一个块大,对于存储二进制文件(或其他大数据项,如图片)的记录,会被存储到磁盘的其他位置,而不是我们划分出的块上,然后在记录中只存储这个二进制文件的存储地址。

还要考虑的问题是记录是定长或者变长的问题。

定长记录

对于这个例子,假设我们给这三个varchar变长字段都按最大长度存储,并且每个字符占用1字节,numeric(8,2)占8字节,那么得到的长度就是\(5+20+20+8=53\)个字节

我们可以按顺序在块中存储这些记录,即第一条记录占用块的前53字节,第二条记录占用块的第二个53字节,像这样

问题:

  • 如果块的大小不是53字节的倍数,那么就会有记录需要横跨两个块
  • 删除一个记录后空下来的53字节我们需要想办法处理,是让其他记录向前移动还是标记这个位置是空的,后面的插入可以直接使用。

第一个问题,直接弃用一个块中最后剩下的部分即可,它肯定小于一条定长记录的长度,否则我们就可以将这条记录塞进去了,所以这个小空间的弃用可以接受。第二个问题,如果需要其他记录向前移动,也有两种办法,第一种是将被删除记录后面的所有记录都向前移动,第二种就是让最后一个记录向前移动到被删除记录的位置。

另一种处理删除产生的空余空间的办法是,标记这个空间是空余的,后面如果进行插入操作时直接插入到这个空余空间。这样就需要在文件头中额外维护一个空余空间表。文件头指向第一个被删除的记录留下的空余空间的地址,这个被删除的记录的空间中我们暂时存储下一个空余空间地址,这样就形成了一个用于记录所有空余空间的链表,称作空闲列表

插入记录时,我们就会将记录插入到文件头所指向的空余空间上,并修改文件头指向下一个空余空间。

如果没有空余空间,那么就存到文件末尾。

变长记录

变长记录的表示

可产生变长记录的情况:

  1. 一个文件中存放不同种类的记录
  2. 一个或多个字段是变长字段
  3. 允许可重复字段,如数组或集合

变长记录可以按照如下方式分成两部分存储,第一部分存储记录中的定长字段,这部分的长度固定,第二部分存储变长字段,然后对于变长字段,我们还需要在一个记录的首部(也就是定长字段之前)存储上每一个变长字段的(偏移量, 长度)。偏移量表示该字段在记录中的起始位置,长度表示它所占用的字节数。

比如下面的例子,instructor记录中有四个字段(ID, name, dept_name, salary),其中前三个是变长字段,第四个是占用8个字节的定长字段。

空位图的作用是记录字段中哪些属性是空,如果salary是空,那么空位图第四位就是1。如果记录中的字段很多,空位图的大小也会变大。

变长记录在块中的放置

分槽的页结构,一般用于在块中组织记录,块头中的信息如下

  1. 记录块中的记录个数
  2. 记录块空闲空间的尾部位置(即离头最近的第一个记录的位置-1)
  3. 一个包含块中所包含的所有记录的位置和大小的数组

所以,在这个结构中,记录从尾部开始向前连续排列,如果插入一条记录,那么从空闲空间的尾部向前分配记录大小的空间给新纪录,并且需要将这个记录的位置和大小添加到块头的记录数组中。

删除一个记录,这条记录在记录数组中被标记为删除状态(如它的大小被设置为-1),还需要将该记录前面所有的记录向后移动,并且更新它们在记录数组中所记录的位置。这样删除所得到的空余空间可以被重用。将记录变长或变短也可以用同样的方式。操作一个4~8KB的数据还是很快的。

分槽的页结构中指针不能直接指向块中的记录,而是要指向块头的记录数组

文件中记录的组织

  • 堆文件组织:一条记录可以放在文件中任何位置
  • 顺序文件组织:根据搜索码顺序排布
  • 散列文件组织:根据元组的部分属性计算出散列值,通过散列值确定存放位置

顺序文件组织

目的是为了快速处理按某个搜索码的顺序排序的记录而设计的。

文件中的记录通过指针连接到按搜索码排序的下一条数据,这样保证了记录之间的逻辑顺序。为了减少文件处理中的块访问数,最好让记录的逻辑顺序和物理顺序保持一致,否则逻辑上相邻的两个数据可能处在不同的块中,这样你检索出一批看似应该相邻的数据就不一定要从多少块中来拿数据了。图中(ID为搜索码)就是逻辑顺序和物理顺序完全一致的情况。

在插入和删除记录时又要维护整个文件的结构。

考虑插入时需要遍历整个链,找到这个文件中按照搜索码排序的前一条记录,然后若此时这个前一条记录所在块中有空闲空间就插入到这个块中,否则插入到溢出块中,并且调整指针。

当物理顺序和逻辑顺序不一致达到一定程度,那么处理效率将十分低下,此时应考虑重组文件,重组文件代价很高,所以需要在系统负载很低的时候进行。当插入很少的情况下,这种组织方式能很好的运行。

多表聚簇文件组织

在处理大型数据的系统中,为了避免多个文件带来过多的IO操作,可能会在一个文件里存入多个关系,但是尽管是这样,大多数数据库还是只会在同一个块中存储同一个关系中的元组。

考虑这样的查询,如果我们的系统允许在同一个块中存储多个关系,那么很可能我们要进行连接查询时需要用到的两个关系中的元组在同一个块中,那么就避免了多次读块将数据从磁盘放到内存中。

select * from instructor natural join department;

最高效的文件结构就是将每个department中的元组存储在dept_name相同的instructor元组的附近,这样它们就在同一个块中了,如果恰好不在同一个块中,那么这个department元组也会在相邻的下一个块中。

多表聚簇文件组织适合处理多表连接查询,但其它类型的查询则会被拖慢,比如

select * from department;

因为每个块存储了多个关系中的数据,那么一个块中的department数据就少了,需要执行查询所必须读取的块就增加了。

数据字典存储

数据库还要存储一些数据库系统本身相关的数据,称为元数据,比如

这种信息一般存储在数据字典(data dictionary)或系统目录(system catalog)中

我的理解就是,很多数据库系统为了简化系统设计,并且让用户可以通过与操作其他库和表完全一致的方法来操作这些元数据,所以将元数据内建在系统中一个特殊的数据库里,比如MYSQL的mysql库。

然后这个库因为需求和其他的用于存储海量数据的库的需求不同,所以它一般要重新设计,采用不同的存储方式,但提供一致的操作接口。

缓冲区

在内存中缓存磁盘中的块。

  1. 缓冲区替换策略:缓冲区没有足够空间容纳新块时,需要清除一个旧块,通常使用LRU算法,即最近访问最少的被移除
  2. 被钉住的块:当一个块上的更新操作正在进行时,不允许将块写回磁盘
  3. 块的强制写出:某些情况下,即使不需要这个块的空间,也要将块强制写回磁盘。
posted @ 2021-10-28 10:49  yudoge  阅读(862)  评论(0编辑  收藏  举报