BerkeleyDB这些年来, 加入了不少新的重大特性,但因为提供的是英文文档,而中文的示例一般写于这些特性之前,所以这些特性很少为人所知. 本文在此作一个简单介绍.
1 数据库分片
以前BerkeleyDB对一个数据库(DB, 相当于关系数据库的一个表)而言,只能存在一个物理文件中. 大约在版本4.8的时候, BerkeleyDB加入了数据库分片的功能, 允许将一个数据库的数据存放在多个分片中, 每个分片可以在文件系统中的不同位置. 通过此功能, 可以将一个数据库的数据分散在多个磁盘中, 利用多盘并发I/O(比如RAID10)的能力, 大大改善多核(或多CPU)多磁盘系统的性能.
分片的设置函数为:
-
int DB->set_partition(DB * db, u_int32_t parts, DBT *keys,
-
u_int32_t (*db_partition_fcn) (DB *db, DBT *key))
当前BerkeleyDB支持两种分片方式, 范围分片和Hash分片, 前者需要设置Keys(db_partition_fcn=NULL), 后者则需要设置db_partition_fcn(keys=NULL), 含义很明显, 不需累述.
BerkeleyDB通过以下函数来指定分片的位置
-
int DB->set_partition_dirs(DB *db, const char **dirs);
数据放置以Round-Robin的方式进行, 所以当目录数少于分片数时, 部分目录可能存在两个甚至多个分片.
当前不足的在于, 现在的分片是固定的, 无法自动扩展. 扩展需要自己写程序来移动数据.
2 大块数据存放
以前, 当在BerkeleyDB中存入的某些数据很大时(比如超过一个Page), BerkeleyDB会创建overflow page来存放这些数据. 当存放诸如视频或者音频,图片等数据时, 一个数据会被分为很多page, 散布在数据库中. 这有两个问题: 1 读回数据很麻烦, 读一个数据需要访问很多的Page, 哪怕是只读很小一部分, 带来的性能开销也很大; 插入也很复杂, 需要按Page大小(比page小一点, 因为page需要一部分header信息)来切开数据, 而后申请页面来存放, 直至放完. 更新数据也只能通过先删除后插入的方式进行; 2 占用表空间, 当前一个表支持2**32个页, 而这种大数据一个就能占据很多也. 结果可能导致数据库能存放的记录不多. 所以应用在存放大数据时, 一般只能走曲线途径, 即自己创建一个文件, 读写文件, 而后将文件名存入BerkeleyDB.
大约在6.0/6.1的时候, BerkeleyDB加入了对BLOB的支持, 允许通过一些设置,将大的数据直接存放为外部文件, 而BerkeleyDB负责文件的存放位置, 负责对文件进行操作,以及将文件同步到其他机器(replication).
相关主要函数有:
-
int DB_ENV->set_blob_threshold(DB_ENV *dbenv, u_int32_t bytes, u_int32_t flags);
-
-
int DB->set_blob_threshold(DB *db, u_int32_t bytes, u_int32_t flags);
-
-
int DBC->db_stream(DBC *dbc, DB_STREAM **dbs, u_int32_t flags);
-
int DB_STREAM->read(DB_STREAM *dbs, DBT *data, db_off_t offset,
-
u_int32_t size, u_int32_t flags);
-
int DB_STREAM->write(DB_STREAM *dbs, DBT *data, db_off_t offset, u_int32_t flags);
默认情况下, 外部文件存放在DB_ENV所在目录的子目录中(__b1), 但也可以通过DB_ENV->set_blob_dir, 以及DB->set_blob_dir来修改. 文件并不是平铺开来的, 而是按照一定的目录层次组织的, 避免一个文件夹下的文件数目膨胀.
3 DB_HEAP存取方法
无论是使用DB_BTREE/DB_RECNO还是DB_HASH等方式, 当存放数据时都可能会碰到节点分裂或者桶分裂(hash)的情形, 而此对性能影响很大. 而且无论是HASH还是BTREE, 存取速度都有一定的限制. 而新提供的DB_HEAP访问方式, 则改善了存取速度, 当然, 访问便利性也下降了. 应用一般需要通过DB_HEAP和其他方式结合才行.
DB_HEAP的工作方式是, 当要存放数据时, 根据所存放数据的大小,寻找空闲空间, 而后将数据存入, 返回一个DB_HEAP_RID告知数据的位置. 类似于malloc在Heap存储区获得空间的方式. DB_HEAP_RID包含page和page内偏移, 寻找数据时, 根据先前获得的DB_HEAP_RID可以非常快的定位到数据(物理位置), 而后获得数据. 此方法可以获得很高的数据插入和查询性能, 尤其适合日志类几乎不需要修改,但需要查询的数据. 目前有些应用将DB_QUEUE和DB_HEAP结合起来, 利用DB_QUEUE存放DB_HEAP_RID(因为定长), 这种方式结合了DB_QUEUE(记录级索引,定长记录,操作很快)和DB_HEAP的优势(可存放不定长记录). 还有一些用法是使用DB_HEAP存放原始数据, 而在数据上用DB_BTREE和DB_HASH来建立二级索引, 所有的查找都是用二级索引.
我的另一篇文章有一些简单的使用DB_HEAP的例子: http://blog.chinaunix.net/uid-20738531-id-2010948.html
阅读(1117) | 评论(0) | 转发(0) |