zoukankan      html  css  js  c++  java
  • BerkeleyDB学习笔记(一)

    一、通用配置

    1. 选择合适的access method(BTree、Hash、Queue or RecNo)
    2. (DB->set_pagesize)选择合适的pagesize,处于性能考虑,最好和os的block size匹配,除非每个key/value的数据很大,如果pagesize过小,会导致overflow page的问。
    3. DB->set_cachesize
    4. DB->set_lorder设置字节序。
    5. DB->set_flag(DB_DUP)设置BTree的存储是否支持duplicate keys,缺省情况不支持重复键。DB->get()只是返回重复键中的第一个键值对,重复键数据的获取只能通过DBC-> get(), 重复键的插入缺省行为是append方式,如果打算自定义需要通过DBC->put(),同时制定DB_AFTER, DB_BEFORE, DB_KEYFIRST or DB_KEYLAST.
    6. DB->set_flag(DB_DUPSORT)设置data为自动排序模式,这将极大的提高searching和join的效率。DB->set_dup_compare可以设置data的自定义排序方法。如果该表示已经设置,BDB则不允许生成duplicate data,即key/value都是相同的。

    二、BTree配置

    1. DB->set_bt_compare, 用户可以自定义key的比较函数

    1 int compare_int(DB* dbp, const DBT *a, const DBT *b) {
    2 int ai, bi;
    3 memcpy(&ai,a->data,sizeof(int));
    4 memcpy(&bi,b->data,sizeof(int));
    5 return ai - bi;
    6 }
    7
    8 //比较前5个字符的比较函数
    9   int compare_dbt(DB* dbp,const DBT *a, const DBT *b) {
    10 int len;
    11 u_char *p1, *p2;
    12 for (p1 = a->data, p2 = b->data, len = 5; len--; ++p1,++p2) {
    13 if (*p1 != *p2)
    14 return (long)*p1 - (long)*p2;
    15 }
    16 return 0;
    17 }



    2. DB->set_bt_prefix,用于前缀比较,其比较规则应和完全比较的规则一致,应用该方法主要便于BDB中的page每次只是存储能够区分任何两个键的差别,从而使每页可以存储更多的数据,提高查询的效率,该函数返回任意两个键比较之后的最小可识别的字符数量,如果两个keys在比较时,比较长度直到长度较小的结尾时仍然相等,则返回较小key的长度加一。

    1 u_int32_t compare_prefix(DB* dbp, const DBT *a, const DBT *b) {
    2 size_t cnt, len;
    3 u_int8_t *p1,*p2;
    4 cnt = 1;
    5 len = a->size > b->size ? b->size : a->size;
    6 for (p1 = a->data, p2 = b->data; len--; ++p1,++p2,++cnt) {
    7 if (*p1 != *p2)
    8 return cnt;
    9 }
    10
    11 if (a->size < b->size)
    12 return a->size + 1;
    13 if (b->size < a->size)
    14 return b->size + 1;
    15 return b->size;
    16 }



    3. DB->set_bt_minkey, 用于设置DB中每个page上可容纳的key/value的最小数量,参考如下公式:
         maximum_size = page_size / (minimum_keys * 2)
         之所以minimum_key乘以2,是因为key和value在page中需要占用两个slot,此例中,如果page_size = 8k, minimum_keys = 2,那么任何key或value超过2k的项都将存入overflow page。
    4. DB->set_bt_compress(dbp,NULL,NULL), 后面两个NULL参数表示使用BDB自带的缺省压缩和解压缩算法,该函数需要在DB被打开之前调用。

    三、Hash配置

    1. DB->set_h_ffactor() 设置填充因子,用于确定每个hash bucket中可能容纳key/value的一个近似值,以决定hash table应该在什么时候增长或缩短。请参照以下设置方式
         (pagesize - 32) / (average_key_size + average_data_size + 8)
    2. DB->set_h_hash(), 设置自定义的hash方法。
    3. DB->set_h_nelem(), 用于设定hash table中可能容纳的element数量,如果设置的比较接近真实情况,将更好的避免由于hash bucket动态增长而带来的性能损失,设置该值时也需要考虑设置fill factor。

    四、Recno和Queue配置

    1. DB->set_re_len(),用于设置fixed-length记录的长度。短于该长度的数据,将用DB->set_re_pad中设置的字符进行尾部填充,长于该值的data,将会导致一个错误发生。 

    五、DB Partitioning

    1. 只用BTree和Hash两种方式可以支持partitioning。
    2. DB->set_partition(),DB->set_partition_dirs(), 这两个函数用于对DB进行partition,他们必须在数据库第一次被open之前调用,一旦partition成功之后,其partition的scheme将不能再改变。其中后面的函数主要用于指定partition文件所在的home directory。分区策略是,可以给partition指定其所包含的key的值,也可以通过回调函数的方式,通过回调函数的返回值来确定该键应该存放的partition。
         a--f to go on partition 0
         g--p to go on partition 1
         q--z to go on partition 2
         如果打算使用数组的方式进行partition,set_partition中回调函数参数为NULL。

    1 void partition_exampe_with_array() {
    2 DBT partKey[2];
    3 int ret;
    4
    5 memset(partKeys,0,sizeof(DBT) * 2);
    6 partKeys[0].data = "g";
    7 partKeys[0].size = sizeof("g") - 1;
    8 partKeys[1].data = "q";
    9 partKeys[1].size = sizeof("q") - 1;
    10
    11 dbp->set_partition(dbp,3,partKeys,NULL);
    12 }


         如果打算使用回调的方式进行partition,set_partition中分区提示数组参数为NULL。

    1 void partition_example_with_callback() {
    2 dbp->set_partition(dbp,3,NULL,db_partition_fn);
    3 }
    4
    5 //参数key实际存放的partition No = ret % number_of_partitions
    6   u_int32_t db_partition_fn(DB* db,DBT* key) {
    7 char* key_data;
    8 u_int32_t ret;
    9
    10 key_data = (char*)key->data;
    11 //根据key_data的值进行自定义运算,从而判定该key应该存放的partition。
    12   ret = 0;
    13 return ret;
    14 }


    3. DB->set_partition_dirs(),该函数必须在数据库创建和打开之前设定,一旦成功设定并打开DB,该值将不可变。推荐设置和partition相同数量的dirs。dir可以通过绝对和相对路径支出,可以位于不同的磁盘。如果DB的打开是基于Environment的,在设置之前,需要保证所设定目录已经存在于DB_ENV->add_data_dir()的列表中。

    六、Secondary Indexes

    1. 可以通过给key/value中value的部分信息建立索引,作为secondary db,其key为期望的索引数据,value为primary db的key。

    1 struct student_record {
    2 char student_id[4];
    3 char last_name[15];
    4 char first_name[15];
    5 };
    6
    7 void example_of_secondary() {
    8 DB *dbp, *sdbp;
    9 int ret;
    10
    11 //open/create primary
    12   if ((ret = db_create(&dbp,dbenv,0))
    13 handle_error(ret);
    14 if ((ret = dbp->dbp,NULL,"student.db",NULL,DB_BTREE,DB_CREATE,0600)) != 0)
    15 handle_error(ret);
    16
    17 //open/create secondary
    18   if ((ret = db_create(&sdbp,dbenv,0)) != 0)
    19 handle_error(ret);
    20 if ((ret = sdbp->set_flags(sdbp,DB_DUP|DB_DUPSORT)) != 0)
    21 handle_error(ret);
    22 if ((ret = sdbp->open(sdbp,NULL,"lastname.db",NULL,DB_BTREE,DB_CREATE,0600)) != 0)
    23 handle_error(ret);
    24 //如果基于已有数据recreate secondary index,需要在associate方法中传入DB_CREATE标志。
    25 if ((ret = dbp->associate(dbp,NULL,sdbp,getname,0)) != 0)
    26 handle_error(ret);
    27 }
    28
    29 int getname(DB* secondary,const DBT* pkey, const DBT* pdata,DBT* skey) {
    30 /*如果在构造skey的数据时,需要自定义组合某些数据信息,此时需要用户函数自行分配内存并copy数据到该缓冲,同时也需要给skey的flag设置DB_DBT_APPMALLOC*/
    31 memset(skey,0,sizeof(DBT));
    32 skey->data = ((struct student_record*)pdata->data)->last_name);
    33 skey->size = sizeof(((struct student_record*)pdata->data)->last_name);
    34 return 0;
    35 }

    2. 如果删除primary中的数据,相关的secondary数据也会自动删除,反之亦然。通过DB->pget和DBC->pget()可以通过secondary的查询获取primary中key和value。但是不能直接在secondary DB中直接插入数据。

    七、DB Cursor


    1. DB_CONSUME标志:Read-and-Delete, 这个标志只能用于Queue类型的DB,头部记录将被返回并删除。
    2. DB->join: 相等性连接。请参照以下示例:

    1 //DB_Personnel { key = SSN data = record contain name, address, phone number,job titile }
    2 //DB_Lastname { key = lastname data = SSN }
    3 //DB_jobs { key = job title data = SSN }
    4
    5 void example() {
    6 DBC *name_curs,*job_curs,*join_curs;
    7 DBC *carray[3];
    8 DBT key,data;
    9 int ret, tret;
    10
    11 name_curs = NULL;
    12 job_curs = NULL;
    13 memset(&key,0,sizeof(key));
    14 memset(&data,0,sizeof(data));
    15
    16 if ((ret = name_db->cursor(name_db,txn,&name_curs,0)) != 0)
    17 goto err;
    18
    19 key.data = "smith";
    20 key.size = sizeof("simth");
    21 if ((ret = name_curs->get(name_curs,&key,&data,DB_SET)) != 0)
    22 goto err;
    23
    24 if ((ret = job_db->cursor(job_db,txn,&job_curs,0)) != 0)
    25 goto err;
    26
    27 key.data = "manager";
    28 key.size = sizeof("manager");
    29 if ((ret = job_curs->get(job_curs,&key,&data,DB_SET)) != 0)
    30 goto err;
    31
    32 carray[0] = name_curs;
    33 carray[1] = job_curs;
    34 carray[2] = NULL;
    35
    36 if ((ret = pers_db->join(pers_db,carray,&join_curs,0)) != 0)
    37 goto err;
    38
    39 while ((ret = join_curs->get(join_curs,&key,&data,0)) == 0) {
    40 //TODO Process record returned in key/data
    41 }
    42
    43 if (ret == DB_NOTFOUND)
    44 return = 0;
    45
    46 err:
    47 if (join_curs != NULL && (tret = join_curs->close(join_curs) != 0 && ret == 0)
    48 ret = tret;
    49 if (name_curs != NULL && (tret = name_curs->close(name_curs) != 0 && ret == 0)
    50 ret = tret;
    51 if (job_curs != NULL && (tret = job_curs->close(job_curs)) != && ret == 0)
    52 ret = tret;
    53 return ret;
    54 }


    八、Bulk获取和更新

    1.  DB_MULTIPLE和DB_MULTIPLE_KEY分别对应于获取所有指定key的duplicate data和多个keys/values
         DB_MULTIPLE_INIT用于最初的初始化调用。
         DB_MULTIPLE_NEXT: 该宏总是和DB_MULTIPLE标志绑定使用。
         DB_MULTIPLE_KEY_NEXT: 该宏总是和DB_MULTIPLE_KEY标志绑定使用,同时要求底层db的类型为BTree or Hash。
         DB_MULTIPLE_RECNO_NEXT: 该宏总是和DB_MULTIPLE_KEY标志绑定使用,同时要求底层db的类型为Queue or Recno。
        在调用DB->get() or DBC->get()的时候,将DBT data参数data字段指向buffer,ulen表示buffer的长度,flags字段需要设置为DB_DBT_USERMEM
         

    1 #define BUFFER_LENGTH (5*1024*1024)
    2 int rec_display(DB *dbp) {
    3 DBC *dbcp;
    4 DBT key,data;
    5 size_t retklen,retdlen;
    6 char *retkey,*retdata;
    7 int ret,t_ret;
    8 void *p;
    9
    10 memset(&key,0,sizeof(key));
    11 memset(&data,0,sizeof(data));
    12
    13 if ((data.data = malloc(BUFFER_LENGTH)) == NULL)
    14 return errno;
    15 data.ulen = BUFFER_LENGTH;
    16 data.flags = DB_DBT_USERMEM;
    17
    18 if ((ret = dbp->cursor(dbp,NULL,&dbcp,0)) != 0) {
    19 dbp->err(dbp,ret,"DB->cursor");
    20 free(data.data);
    21 return ret;
    22 }
    23
    24 for (;;) {
    25 if ((ret = dbcp->get(dbcp,&key,&data,DB_MULTIPLE_KEY | DB_NEXT)) != 0) {
    26 if (ret != DB_NOTFOUND)
    27 dbp->err(dbp,ret,"DBcursor->get");
    28 break;
    29 }
    30
    31 for (DB_MULTIPLE_INIT(p,&data);;) {
    32 DB_MULTIPLE_KEY_NEXT(p,&data,retkey,retklen,retdata,retdlen);
    33 if (p == NULL)
    34 break;
    35 printf("Key: %.*s, Data: %.*s\n",(int)retklen,retkey,retdlen,retdata);
    36 }
    37
    38 if ((t_ret = dbcp->close(dbcp)) != 0) {
    39 dbp->err(dbp,ret,"DBcursor->close");
    40 if (ret == 0)
    41 ret = t_ret;
    42 }
    43
    44 free(data.data);
    45 return ret;
    46 }
    47 }

  • 相关阅读:
    JavaScript脚本语言特色时钟
    这个周末安排,
    市场营销书籍推荐
    比较好的管理类书籍推荐
    如何培养自己的领导力?或许你该看看这些书
    十本最畅销的市场营销书籍,你看过几本?
    如何提高情商?答案可能在《情商必读12篇》这本书里
    如何管理好员工?
    做销售要看哪些书?《销售管理必读12篇》了解下
    管理书籍推荐,你看过哪些呢?
  • 原文地址:https://www.cnblogs.com/orangeform/p/2023049.html
Copyright © 2011-2022 走看看