Berkeley DB 学习笔记

Berkeley DB 的数据库 目前提供四种访问方式:Btree, Hash, Queue和Recno。每个数据库是以什么样的方式创建的,以后就必须用什么样的方式访问。
对用户而言,Berkeley DB内存储的都是key-pair这样的键值对。
对于Btree,在插入、删除一条记录的时候,其它记录的record number也会随之而改变。而Queue不变。Recno可设置为改变或不改变(默认是不改变)。
Hash不支持Logical record number。
Btree:
采用已排序的、平衡树的结构存储数据。查询、插入、删除的效率都是0(logN)。
既支持按key查找,也支持按record number查找。
Hash:
以hash的结构存储数据。采用的方式是“Extended Linear Hashing”
Queue和Recno只存储data,key是由数据库内部产生一个logical record number来唯一标识每条记录。
Queue:
每条记录的长度固定。(即每条记录的最大长度必须实现已知)
可以使用get方法从队列的头部原子化的获取并删除记录。
支持记录级别的锁,因之比Recno具有更高的并发度。
在插入、删除一条记录的时候,其它记录的record number不会改变。
Recno:
每条记录的长度固定或不定。
可采用一个flat text file做后存储。
如果记录的长度不定,且采用了一个flat text file做后存储,则需要设置一个特殊的字节做为记录的结束符。如果没有设置,默认是'\n'。如果有多个进程要同时向其插入/删除数据,则需要一次把整个后存储文件读入,否则只在调用get的时候读入必须的行。而且应该只有一个进程设置了flat text file,且该进程需要设置DB_SNAPSHOT参数。
对于二进制数据,可以采用出现频率最小的数字(d)做为结束符,在插入的时候把原始数据中出现d的地方后面再加一个d。
例如以下的代码向/tmp/make.conf中追加一行

int main(int argc,char* argv[]){
    setlocale(LC_ALL,"");
    DB* db;
    db_create(&db,NULL,0);
    ///此处应该先ftrylockfile /tmp/make.conf.lock
    db->set_re_source(db,"/tmp/make.conf");
    db->set_flags(db,DB_SNAPSHOT);

    db->open(db,NULL,"/tmp/redb.db",NULL,DB_RECNO,DB_CREATE,0);
    DBT dbt,key;
    memset(&dbt,0,sizeof(DBT));
    memset(&key,0,sizeof(DBT));
    char s[]="CC=gcc";
    dbt.data=s;
    dbt.size=sizeof(s)-1; //strlen(s)

    db->put(db,NULL,NULL,&dbt,DB_APPEND);
    db->close(db,0);
}

如果对后存储文件只读不写,则可在db->open的时候把db的路径名设置为NULL以获取更高的效率,此时一般采用匿名的mmap方式,因为不进行硬盘IO,所以效率会有明显的提升。
Secondary indices
建索引的一种手段。
Secondary DB的key必须可以通过函数从Main DB的key和data中得到,其data由db使用Main DB中对应记录的key填充
如果使用了环境,而进程被意外杀死或退出,导致退出前没有调用DB_ENV->close(),那么存储在__db.*文件中的数据将可能发生不一致的现象。当再次打开环境的时候,可能会得到这样的错误
[2007-09-18 21时43分34秒] Cannot allocate memory
[2007-09-18 21时45分03秒] unable to allocate memory for mutex; resize mutex region
解决方法是:
1、如果使用了事务(TXN),那么带上DB_RECOVER标志调用DB_ENV->open。注意,同一个时间只能有一个进程在进行 DB_RECOVER,期间所有的数据库操作应当被停止。且,进行 RECOVER 后,其它所有正在使用该环境的进程必须重新打开环境。
2、如果没有使用,那么使用DB_ENV->remove()删除环境并重建该环境。
对于只读不写的数据库,建议:
1、每次打开环境前都先删除__db.*文件
2、或者,在调用DB_ENV->open()的时候加上DB_PRIVATE 参数

对于事务系统,执行recovery的方法

如果在DB_ENV->open中,DB_REGISTER 标志被设置,那么每个以这样方式打开环境的进程首先会检查是否需要对环境进行reconvery。
如果因为任何原因需要进行recovery(包括初始化创建一个新环境),那么,
1、如果 DB_RECOVER 被设置,该进程将会执行recovery操作,之后正常的打开该环境。
2、如果 DB_RECOVER 没有被设置,DB_ENV->open将会DB_RUNRECOVERY。
如果并不需要进行 recovery,那么DB_RECOVER标志将会被忽略。
如果要DB_REGISTER起效,还要求,
1、所有使用该环境的进程都必须使用DB_REGISTER打开环境。
2、每个进程只能有一个DB_ENV句柄。

对于没有使用事务的系统

首先,所有进程关闭其db handle
然后,所有其它进程关闭其db_env handle
然后,执行db_remove
然后,重新打开所有的环境和db_handle

使用了DB_CONSUME_WAIT的时候必须在ENV中初始化lock环境。
目前版本的DB_CONSUME_WAIT有一个问题是,假如有10个进程在等待一个进程往数据库里写数据,那么当事件发生后,尽管该进程是写入了一条记录而引发了事件,但是唤醒的是10个线程,然后一个工作,9个再度休眠。这导致系统的running队列总是特别长。

此博客中的热门博文

少写代码,多读别人写的代码

在windows下使用llvm+clang

tensorflow distributed runtime初窥