现在的位置: 首页 > 综合 > 正文

dbm数据库源代码分析(3):头文件部分

2013年11月26日 ⁄ 综合 ⁄ 共 12209字 ⁄ 字号 评论关闭

   现在解剖gdbm的头文件部分的源码。
   (1)autoconf.h:由configure脚本根据autoconf.h.in模板生成。autoconf.h为平台相关的的头文件、函数、库等定义常量标志(只有这些常量标志,没有其他任何头文件和代码),这样在你的源代码中就可以用预编译指令来引用这些常量标志(通过#include "autoconf.h"),以便能够构建出可移植的程序。

   (2)system.h:包含平台相关的头文件和代码。一些函数(如文件锁操作)有的平台上有,有的平台上没有(必须通过更底层的函数来实现),而有时又可能不同平台上的名称不同。system.h把这些函数和代码编写成统一的接口,以供程序使用,这样就隐藏了底层不同平台的实现差异。

   (3)gdbmerror.h:gdbm的错误码列表。主要有内存分配、文件读写、文件锁定、数据库操作方面的错误。

   (4)extern.h:gdbm函数需要的一些全局变量声明。如文件信息结构、firstkey和fetch等要用到的一些全局变量等。

   (5)gdbmconst.h:gdbm要用到的一些常量定义。包括gdbm_open、gdbm_store、gdbm_setopt的一些常量参数,桶缓存的大小等。

   (6)gdbmdefs.h:包含了gdbm数据库文件的结构。这些定义具有兼容性,它们既用在gdbm中,也用在dbm、ndbm中。理解这些结构体是理解gdbm怎样实现的关键。

   一般数据库的实现大体思路就是用一个数据文件来存放数据,这个数据文件其实就是普通的文件,但是文件的结构可能设计的很复杂。另外,也可以拿一个文件来保存索引,如果数据量不是非常大,可以把索引全都放在内存里,例如mysql的heap型数据库就是这么做的。gdbm数据库的存储方式采用的是可扩展散列表。
   先解释一下散列技术。散列技术的核心是散列函数。散列函数是一种将键值映射为散列表中的存储位置的函数。对任意给定的动态查找表T,如果选定了某个"理想的"散列函数H及相应的散列表L,则对T中的每个数据元素X,函数值H(X.key)就是X在散列表L中的存储位置。插入(或建表)时数据元素X将被安置在该位置上,并且查找X时也到该位置上去查找。由散列函数决定的数据元素在散列表中的存储位置称为散列地址。因此,散列的基本思想是通过由散列函数决定的键值与散列地址之间的对应关系来实现存储组织和查找运算。按散列存储方式构造的存储结构称为散列表。
   gdbmdefs.h主要的数据结构(桶缓存数组和散列目录表是为了便于理解另加的,可参看具体的源代码实现文件):
   封装数据或关键字的datum结构:数据起点指针、数据长度
   文件信息结构gdbm_file_info:文件名、一些读写模式标志、文件锁标志、错误处理函数的指针、文件描述符、文件头的指针、散列表目录的指针、桶缓存数组指针、当前桶指针、当前缓存项的指针、当前目录项的索引、一些记帐信息。
   文件头gdbm_file_header:魔数、最佳传递块大小、散列目录表的偏移地址、目录项的大小、目录地址占用的比特数、桶中的元素个数、下一个未分配的块的偏移地址、可用块。
   散列桶hash_bucket:可用块元素的个数、可用块元素列表、标识桶的二进制系列、桶中的元素个数、桶的元素列表。 桶元素列表才是真正的可扩展散列表,它会随着文件的增长而分裂。可用块元素中包含了可用存储块的地址和大小,桶元素中包含了我们要管理的关键字/数据的地址和大小,其他的域都是一些记帐信息。
   桶元素bucket_elem:哈希值、关键字的前SMALL个字节、关键字记录的偏移地址、关键字的大小、数据的大小。
   可用块avail_block:可用块元素的大小、可用块元素的个数、下一个可用块的偏移地址、可用块元素列表。
   可用块元素avail_elem:可用块的大小、可用块的偏移地址。注意磁盘文件上的各个可用存储块本身的位置不会有任何改变,而是通过操作指示可用块的avail_elem元素来对可用块进行存取。
   桶缓存数组bucket_cache[size]:由多个缓存项组成的一个数组,在桶缓存初始化时,文件信息结构dbf中有指针bucket_cache指向它。
   桶缓存中的缓存项cache_elem:指向实际桶的指针、偏移地址、更改标志、数据缓存。 注意缓存中的缓存项(cache_elem)不仅包含了实际的桶,还包含了数据缓存块及一些标志。在桶缓存初始化时,dbf中的指针cache_entry初始时指向桶缓存中的第一个缓存项。
   缓存项中的数据缓存元素data_cache_elem:哈希值、数据长度、关键字长度、指向数据起点的指针、偏移位置值。
   散列桶目录表dir[dir_size/4]:每个元素是一个off_t类型,根据元素中存放的偏移地址值访问磁盘上相应的散列桶。文件信息结构dbf中有指针dir指向它。
   各个数据结构之间的关系如下图:

dbm各数据结构间的关系

 


   

   注意存放在磁盘上的一个新的数据库文件只包括文件头gdbm_file_header、桶目录表dir[dir_size/4]、初始的一个桶hash_bucket(以后随着文件的增长会分裂为多个桶),它们按顺序依次存放。这里gdbm_file_header中存放了活动的可用块avail_block以及下一个未分配的可用块的偏移地址,avail_block中的avail_elem才真正指出了数据存储块的大小和偏移地址。hash_bucket中的bucket_element有指向关键字/数据的指针data_pointer(注意数据直接存储在关键字之后),关键字大小及数据的大小,同时还含有关键字的一小部分实际值。其他结构都是操作这个文件的一些工具(一般在内存中)。用户对数据库文件的所有访问和存取操作都是通过gdbm_fine_info结构来完成的。
   为了高效地对数据进行存取,这里实现了一个缓存系统,主要有缓存项cache_elem结构和数据缓存元素data_cache_elem结构。cache_elem中有指向实际的桶的指针(桶在磁盘上,用来对数据进行定位、查找、存取等),还包含了数据缓存元素及一些标志。数据缓存元素中指明了我们要存取的实际数据,通过内存中的缓存系统,我们可以大大提高数据的存取速度。

抱歉!评论已关闭.