• Berkeley DB (二) Access Method


    Berkeley DB -- Access Method Configuration




    Hash or Btree?

    Hash 和 Btree方法应该被用于当逻辑记录号不是用来做主键对数据访问的情况。(如果逻辑记录号是一个secondary key,用来对数据进行访问,Btree方法是一个可能的选择,因为它支持通过一个键和一个记录号来同时的访问。)

    Btrees中的键是按一定的秩序来存放的。Btrees应该被用于那些keys存在某种关系的时候。例如用时间做keys,当现在访问8AM时间戳的时候,可能下一个就访问9AM时间戳。也就是在排列顺序中附近的(near)。再比如,用names做keys,我们也许要访问那些有相同last name的,Btrees仍然是一个不错的选择。

    在小的数据设置上,Hash 和 Btree在性能表现上没什么差别。在那儿,所有的,或大部分数据设置被放在了cache里面。

    尽管如此,当一个一数据设置足够大的时候,会有一些重要的数据页再也装不进cache里了。这种情况下,我们上面讨论的btree在性能表现上就很重要了。
    例如,因为在hash中没有排列顺序中附近的机制。所以,cache在Btree中通常比Hash中更有效。Btree方法将产生更少的I/O调用。

    尽管如此,当一个数据设置更大的时候,hash访问方法能赢过btree方法。原因是btree比hash数据库包含了更多的元数据页。
    数据设置可以变的非常大,以至于元数据开始支配整个cache。如果这种事情发生,Btree将不得不对每次请求都进行一次I/O操作。Cache中几乎没有地方再放置那些真正的数据页了,失去了cache的意义。而因为hash有很少的元数据,可以它的cache照样可以用来放置那些数据页,起到cahche的作用。

    当一个数据更更大的时候,以至于每个随机请求,hash和btree几乎都要进行一次I/O操作的时候。在这中情况下,实际上hash只要遍历少树几个内部页(internal pages)就差不多能找到,所以这也是hash在性能上的一个优势。

    应用程序对数据的访问式样也极大的影响这些行为。例如,延着光标往下遍历的话,每次I/O操作到cache中的数据,将满足接下来的很多数据请求。

    如果数据设置只是比cache大一点,我们还是条件使用Btree,如果你实在有太大的数据设置,hash也许会更好一些。db_stat公用程序是一个有用的工具,用来监视,你的cache表现的怎么样。

    总结:
    其实到这你应该能看出来,btree是在数据不是很大的时候是很优秀的,在更大的时候,由于元数据占用太多cache的原因,导致性能下降,落后与hash了,而不是说hash能超过它。所以能在元数据占用cache不是太多以前,也就是你的cache足够大,使用btree只最好的选择。当然,如果每次访问的数据都是随机的没有什么次序,也不是near的,那用btree也没什么优势了。

    针对我们的应用我只讨论了 Hash or Btree?。Queue or Recno?我就不再讨论了。



    选择一个页的大小:
    太大了会产生很多不必要的i/o,而且影响并发性,因为Btree, Hash and Recn都是对页上锁。太小了会使用溢出页,大量使用溢出页会严重影响性能。所以一般
    页的大小都选择和文件系统的I/O块,大小相等。



    选择一个cache大小:
    要设置的足够大,至少能满足一次操作的数据。如果你的cache设的太小,每个新页将要强迫换出least-recently-used page。
    Berkeley DB将要重新读一次树的root page对于每次数据库请求。当然cache也不是越大越好,当cache大小增长到一个特定的点时,再增加就不会对性能有什么提高了。当到达这个点时,两件事情发生了。Cache足够大以至于,几乎所有的请求都不用再访问磁盘了就能从cache中得到信息。或则是你的应用程序做一些确实很随机的访问,因此再增加cache对于下一个请求也不会有什么性能上的提高了。第二种情况发生的概率很小,因为几乎所有的应用,都显示了一些,请求的相关联性。
    如果cache设定的超过了操作系统的能力,将会使用交换分区,频繁换入换出,会很影响性能。


    Berkeley DB -- Access Method Wrapup



    觉得有必要先把DBT结构放在这。方便后面看。
    typedef struct {
    void *data;
    u_int32_t size;
    u_int32_t ulen;
    u_int32_t dlen;
    u_int32_t doff;
    u_int32_t flags;
    } DBT;

    1. 数据对齐
    Berkeley DB没有为以DBT为参数的,返回的data/key对,或回调函数的字节对齐提供任何保证。
    应用程序有责任对齐任何需要对齐的。DB_DBT_MALLOC, DB_DBT_REALLOC 和 DB_DBT_USERMEM标志可能被用来对齐存储在内存中的返回项。

    2. 在bulk中取回数据
    当从数据库中取回大量记录的时候,那些方法调用经常影响性能。Berkeley DB提供bulk取数据接口,它能有效的提高一些应用持续的性能要使用bulk,必须先为DB->get或DBcursor->c_get指定一个buffer。这个在c api中的实现是通过设置DBT结构的data和ulen域还有flag域被设为DB_DBT_USERMEM来引用应用程序的buffer。DB_MULTIPLE或DB_MULTIPLE_KEY 需要指定给DB->get或 DBcursor->c_get方法, 以使多条记录被返回到指定的buffer中。这两个标志的区别请看手册。
    下面函数只看红色标出部分就可以了。示范如何使用bulk。
    ...................................................................................
    int rec_display(DB *dbp)
    {
    DBC *dbcp;
    DBT key, data;
    size_t retklen, retdlen;
    char *retkey, *retdata;
    int ret, t_ret;
    void *p;
    memset(&key, 0, sizeof(key));
    memset(&data, 0, sizeof(data));
    /* Review the database in 5MB chunks. */
    #define BUFFER_LENGTH (5 * 1024 * 1024)
    if ((data.data = malloc(BUFFER_LENGTH)) == NULL)
    return (errno);
    data.ulen = BUFFER_LENGTH;
    data.flags = DB_DBT_USERMEM;
    /* Acquire a cursor for the database. */
    if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
    dbp->err(dbp, ret, "DB->cursor");
    free(data.data);
    return (ret);
    }
    for (;;) {
    /*
      * Acquire the next set of key/data pairs. This code does
      * not handle single key/data pairs that won't fit in a
      * BUFFER_LENGTH size buffer, instead returning DB_BUFFER_SMALL
      * to our caller.
      */
    if ((ret = dbcp->c_get(dbcp,
        &key, &data, DB_MULTIPLE_KEY | DB_NEXT)) != 0) {
      if (ret != DB_NOTFOUND)
      dbp->err(dbp, ret, "DBcursor->c_get");
      break;
    }
    for (DB_MULTIPLE_INIT(p, &data);;) {
      DB_MULTIPLE_KEY_NEXT(p,
        &data, retkey, retklen, retdata, retdlen);
      if (p == NULL)
      break;
      printf("key: %.*s, data: %.*s\n",
        (int)retklen, retkey, (int)retdlen, retdata);
    }
    }
    if ((t_ret = dbcp->c_close(dbcp)) != 0) {
    dbp->err(dbp, ret, "DBcursor->close");
    if (ret == 0)
      ret = t_ret;
    }
    free(data.data);
    return (ret);
    }
    ................................................................................................


    3. 记录的部分的存储和取回
    在Berkeley DB的访问方法中,可以只存储或取回数据项的某一部分。这个通过设置DBT结构的DB_DBT_PARTIAL 标志来实现。
    同时还要设置DBT的其他几个值。
    doff 数据开始处
    dlen 数据长度
    例如,如果数据项是ABCDEFGHIJKL, doff的值为3是指从字节D开始。dlen为4,是指随后的4个字节DEFG。
    取回记录:
    当从一个数据库中取回一个数据项时,从doff位置开始的dlen字节,被返回。如果被指定的那些字节不存在,其他存在的字节将被返回。
    存储记录:
    下面的例子初始化数据项字节长度都是20: ABCDEFGHIJ0123456789
    1,
    size = 20
    doff = 0
    dlen = 20
    data = abcdefghijabcdefghij
    Result: The 20 bytes at offset 0 are replaced by the 20 bytes of data;
    that is, the entire record is replaced.
    ABCDEFGHIJ0123456789 -> abcdefghijabcdefghij
    2,
    size = 10
    doff = 2
    dlen = 15
    data = abcdefghij
    Result: The 15 bytes at offset 2 are replaced by the 10 bytes of data.
    ABCDEFGHIJ0123456789 -> ABabcdefghij789
    2,
    size = 10
    doff = 25
    dlen = 0
    data = abcdefghij
    Result: The 0 bytes at offset 25 are replaced by the 10 bytes of data;
    that is, 10 bytes are inserted into the record past the end of the
    current data (\0 represents a nul byte).
    ABCDEFGHIJ0123456789 -> ABCDEFGHIJ0123456789\0\0\0\0\0abcdefghij
    其实就是字符串替换,把数据库中某条记录的某部分替换成指定的字符串,长度可以自动根据被替换的字符串大小进行调整。可伸缩的。

  • 相关阅读:
    java性能优化之HashMap,LinkedHashMap,TreeMap读取大量数据效率的比较
    jdk8 stream实现sql单表select a,b,sum(),avg(),max() from group by a,b order by a,b limit M offset N及其性能
    postgresql cstore_fdw性能测试
    Oracle JDBC prefetch: how to avoid running out of RAM
    mysql-创建用户报错ERROR 1396 (HY000): Operation CREATE USER failed for 'root'@'localhost'
    kafka外部访问设置
    mysql 排序长度限制之max_length_for_sort_data以及mysql两种排序算法
    mybatis三个执行器的差别
    Dubbo的集群容错与负载均衡策略及自定义(一致性哈希路由的缺点及自定义)
    mysql中包含长字段索引的优化
  • 原文地址:https://www.cnblogs.com/huqingyu/p/522252.html
Copyright © 2020-2023  润新知