• BerkeleyDB学习笔记(二)


    九、部分记录读取和存储

    1.  在进行数据操作的时候需要为DB->put() or DB->get()的DBT data参数的flags字段设定DB_DBT_PARTIAL标志。同时还要指定doff(偏移量)和dlen(长度)。如"ABCDEFGHIJ",如果doff=3 and dlen=4,其所操作的字符串为"DEFG"。在进行数据部分替换的时候,DB->put()将使用data的data字段和size字段表示的数据替换其doff字段和dlen字段所包含的数据,如果size大于ulen,该key的data item将增大,否则data item将缩小。

    2. 在使用DB->get() or DBC->get()获取数据的时候,其返回的data.data指针指向实际包含的数据,由于该指针所指向的地址在缺省情况下是有BDB内部自行分配的,因此当有任何对BDB的handle操作时,该地址将变为无效,这其中包括多线程中的争用。如果打算避免此类问题的发生,需要自行分配内存空间,同时将DBT data的flags标志设定DB_DBT_USERMEM即可。

    十、 错误支持

    1. DB->db_strerror(error_ret): 其功能和用法类似于Ansi C中的strerror,只是该函数不仅能够处理system errors,也能够处理BDB特定的errno。
    2. DB->db_setpfx(),    为后面调用DB->db_err()和DB->db_errx()函数输出错误信息时,提供一致性的前缀。
    3. DB->db_err(), 其用法类似printf,只是还会为第二个参数中给出的错误返回值打印出其错误消息。
    4. DB->db_errx(), 其用法类似printf,但是比db_err()更简单。

    1 #define DATABASE "access.db"
    2 void errorExampe() {
    3 int ret;
    4 (void)dbp->set_errpfx(dbp, program_name);
    5
    6 if ((ret = dbp->open(dbp,NULL, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
    7 dbp->err(dbp, ret, "%s", DATABASE);
    8 dbp->errx(dbp, "contact your system administrator: session ID was %d", session_id);
    9 return 1;
    10 }
    11 }

       输出结果:
       my_app: access.db: Permission denied.
       my_app: contact your system administrator: session ID was 14 

    十一、 Little Endian对Key排序的影响

         假设用户存放254到257这4个数字以Little Endian的方式put到DB中,其内存布局为:
         254 fe 0 0 0
         255 ff 0 0 0
       256  0 1 0 0
       257  1 1 0 0

       如果将他们视为缺省的lexicographical排序方式,其排序结果如下:
       256  0 1 0 0
       257  1 1 0 0
         254 fe 0 0 0
         255 ff 0 0 0

         如果在Big Endian的体系结构中,他们的内存布局为:
       254 0 0 0 fe
       255 0 0 0 ff
       256 0 0 1 0
       257 0 0 1 1
       结论:推荐使用Big Endian的方式存储整型数据,以便使Tree更加紧凑。
      
       对于数据库文件本身,其byte order是相对独立的,因为在多个拥有不同byte order的machine中copy这些数据文件是没有问题,字节序的处理细节均需交给程序本身来完成。log文件的字节序是有依赖的,因为只能在拥有相同byte order的主机之间进行copy。

    十二、Env相关的设置


         1. 环境的目录信息设置。
         dbenv->set_lg_dir(dbenv,"logdir");
         dbenv->set_data_dir(dbenv,"datadir");
         dbenv->set_tmp_dir(dbenv,"temp");
         dbenv->open(dbenv,"/a/database",flags,mode);
         以上代码将日志文件存放于/a/database/logdir,数据文件存放于/a/database/datadir, 临时文件存放于/a/database/temp.

    十三、事物

         1. DB_TXN_WRITE_NOSYNC and DB_TXN_NOSYNC标志,将会让事物的commit操作避免了同步的disk IO,从而极大的提高了事物的性能,也同时降低了死锁的风险。
         
         2. 当有多个简单的事物操作同时发生时,如DB->put(),是不会有死锁产生的,因为他们同一时间只是持有一把锁,复杂的事物由于会在很长的时间内同时持有多把锁,因此,如果同时存在多个复杂的事物将会极大的提高死锁的几率。如果出现该类情况,可以考虑将复杂的事物划分为多个子嵌套事物,以便缓解死锁带来的问题。
         
         3. 数据隔离性:下面的事例用于表示多个线程同时从DB中读取同一条记录,并且将其data加一后回写。如果此时没有事物的隔离性保护,由于是并发的在同一条记录进行操作,因而可能会产生race condition,最终会导致错误的结果。

    1 int add_color(DB_ENV* dbenv,DB* dbp,char* color, int increment) {
    2 DBT key, data;
    3 DB_TXN* tid;
    4 int fail,original,ret,t_ret;
    5 char buf64;
    6
    7 memset(&key,0,sizeof(key));
    8 key.data = color
    9 key.size = strlen(color);
    10 memset(&data,0,sizeof(data));
    11 //设置该标志用于提示BDB负责分配内存(based on malloc function),由caller决定何时通过free释放该内存,
    12 //对于多线程同时操作的情况,应该使用该方式,从而避免在BDB的内部多个线程共享同一地址空间。该标志还可以带来
    13 //一定的效率提升,由于只是进行了一次内存分配,caller即可以自由的使用,并根据自己的情况决定何时释放,相比于
    14 //传统的local memory方式,数据持有者将节省了一次内存copy的工作。
    15   data.flags = DB_DBT_MALLOC;
    16
    17 for (fail = 0;;) {
    18 if ((ret = dbenv->txn_begin(dbenv,NULL,&tid,0)) != 0) {
    19 dbenv->err(dbenv,ret,"DB_ENV->txn_begin");
    20 exit(1);
    21 }
    22
    23 //DB_RMW标志表示在读取数据的时候即直接获取写锁,因为该代码逻辑是获取指定key的数据,加一后回写,
    24 //因而最终还是有写操作放生在同一记录上,使用该标志可以降低从读锁到写锁升级过程中而带来的死锁可能。
    25   switch (ret = dbp->get(dbp,tid,&key,&data,DB_RMW)) {
    26 case 0:
    27 original = atoi(data.data);
    28 break;
    29 case DB_LOCK_DEADLOCK:
    30 default:
    31 if ((t_ret = tid->abort(tid)) != 0) {
    32 dbenv->err(dbenv,t_ret,"DB_TXN->abort");
    33 exit(1);
    34 }
    35 if (fail++ == MAXIMUM_RETRY)
    36 return ret;
    37 continue;
    38 case DB_NOTFOUND:
    39 original = 0;
    40 break;
    41 }
    42 if (data.data != NULL)
    43 free(data.data);
    44
    45 snprintf(buf,sizeof(buf),"%d",original + increment);
    46 data.data = buf;
    47 data.size = strlen(buf) + 1;
    48
    49 switch (ret = dbp->put(dbp,tid,&key,&data,0)) {
    50 case 0:
    51 if ((ret = tid->commit(tid,0)) != 0) {
    52 dbenv->err(dbenv,t_ret,"DB_TXN->commit");
    53 exit(1);
    54 }
    55 return 0;
    56 case DB_LOCK_DEADLOCK:
    57 default:
    58 if ((t_ret = tid->abort(tid)) != 0) {
    59 dbenv->err(dbenv,t_ret,"DB_TXN->abort");
    60 exit(1);
    61 }
    62 if (fail++ == MAXIMUM_RETRY)
    63 return ret;
    64 break;
    65 }
    66 }
    67 }

         4. 死锁检测通过两种方式进行设置,其一是在dbenv初始化的时候调用该函数 dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT),也可以通过一个独立的线程定时的执行dbenv->lock_detect()方法,前者将会使每次出现conflict的时候都会执行死锁检测,因此会影响并发的性能,后者是在一个单独的线程中定时的完成,所以效率更好。
         
         5. checkpoint,定时的执行dbenv->txn_checkpoint()函数,可以将日志文件中最近更新的数据(上一次执行该函数之后)直接flush到database文件中去。执行checkpoint操作的频率越高,DB_RECOVERY的操作完成的越快。
       
         6. 数据备份,主要分为标准备份和热备份两种。
            标准备份的步骤:
            1) 提交或者放弃所有当前的事物;
            2) 停止所有写操作,读操作可以进行,知道备份完毕;
            3) 在该DBENV上强制执行一个checkponit操作
               4) 运行db_archive工具加-s选项,列出所有当前可用的数据库文件,copy他们到备份介质,推荐将database files放到单独的目录,这样可以直接copy该子目录。
               5) 运行db_archive工具加-l选项,列出所有当前可用的日志文件,copy最后一个日志文件到备份介质。
              
               热备份的步骤:
               1) 在环境中设置DB_HOTBACKUP_IN_PROGRESS标志,其将影响DB_TXN_BULK的行为,致使Bulk操作可以产生日志数据。
               2) 完成标准备份中的第四步骤,确保你使用的copy工具,可以对database page进行原子性copy。
               3) 归档所有的日志文件,将其copy到备份介质,鉴于此,推荐将日志文件存放到环境主目录下的一个单独子目录。注意,一定要保证copy的顺序是先copy数据文件,再copy日志文件,特别是当数据文件和日志文件存放于同一目录下时。
               4) 恢复DB_HOTBACKUP_IN_PROGRESS.
               5) 为了缩短日志的copy时间,可以在hotbackup之前,通过db_archive工具识别出不在使用的日志文件,将其移出当前的目录。
              
         7. 日志移除
            1) db_archive -d 将移除所有不在使用的日志文件。
            2) 调用DB_ENV->log_archive()函数,同时将flags参数设置为DB_ARCH_REMOVE,其效果同1)。
            3) 调用DB_ENV->log_set_config()函数,同时将flags参数设置为DB_LOG_AUTO_REMOVE,这样BDB将自动移除所有不在使用中的日志文件,应用程序同样也没有机会copy这些日志文件到备份介质。

    十四、 Replication

       1. 所有被复制的数据库文件必须位于DB_ENV的主目录下面,或者是位于DB_ENV->set_data_dir()中指定的目录中,不能位于其子目录内。
       2. 环境初始化必须包含DB_INIT_REP和DB_THREAD两个标志。
       3. 该结构的DB_ENV必须是支持事务的。
       4. 调用DB_ENV->repmgr_set_local_site()、DB_ENV->repmgr_set_ack_policy()和DB_ENV->repmgr_add_remote_site(), 以便初始化复制的通讯层,最后调用DB_ENV->repmgr_start()方法启动复制管理器。
       5. 其他和Replicatio Manager方式相关的函数:
          1) DB_ENV->rep_set_nsites() 设置Replication Group中BDB的数量,以便用于master失败后的election。
          2) DB_ENV->rep_set_priotiry() 设置优先级,该值越高,在竞选master的过程中,成为master的可能性越高。
          3) DB_ENV->rep_set_timeout() 设置master和slaves之间的heartbeat超时,如果指定时间内没有收到master的HeartBeat,election将被启动。

  • 相关阅读:
    学习bootsect.s中经常会问到的问题
    c++资源之不完全导引(全文) (转载)
    bootsect.S (读核笔记系列)
    WAP开发FAQ
    学习使用groovy(翻译稿之第一章)
    "革命尚未成功,同志仍需努力!"
    JAVA中this用法小结(转)
    Android <Button>样式的设置方法
    最新SDK android开发环境搭建
    Android中的数据存取(一)Preference
  • 原文地址:https://www.cnblogs.com/orangeform/p/2073303.html
Copyright © 2020-2023  润新知