【问题标题】:Insert MANY key value pairs fast into berkeley db with hash access使用散列访问将许多键值对快速插入 berkeley db
【发布时间】:2010-05-27 09:17:51
【问题描述】:

我正在尝试使用 berkeley db 构建一个哈希,其中应包含许多元组(大约 18GB 的​​键值对),但在我的所有测试中,插入操作的性能会随着时间的推移而急剧下降。我写了这个脚本来测试性能:

#include<iostream>
#include<db_cxx.h>
#include<ctime>

#define MILLION 1000000

int main () {
    long long a = 0;
    long long b = 0;

    int passes = 0;
    int i = 0;
    u_int32_t flags = DB_CREATE;

    Db* dbp = new Db(NULL,0);
    dbp->set_cachesize( 0, 1024 * 1024 * 1024, 1 );

    int ret = dbp->open(
            NULL,
            "test.db",
            NULL,
            DB_HASH,
            flags,
            0);
    time_t time1 = time(NULL);

    while ( passes < 100 ) {
        while( i < MILLION ) {

            Dbt key( &a, sizeof(long long) );
            Dbt data( &b, sizeof(long long) );

            dbp->put( NULL, &key, &data, 0);
            a++; b++; i++;  
        }

        DbEnv* dbep = dbp->get_env();
        int tmp;
        dbep->memp_trickle( 50, &tmp );

        i=0;
        passes++;
        std::cout << "Inserted one million --> pass: " << passes << " took: " << time(NULL) - time1 << "sec" << std::endl;
        time1 = time(NULL);
    }

}

也许你可以告诉我为什么一段时间后“放置”操作花费的时间越来越长,也许如何解决这个问题。

感谢您的帮助, 安德烈亚斯

【问题讨论】:

  • 你为什么使用Java风格?为什么不只是Db dbp(NULL, 0)
  • 无特殊原因。无论如何,在这种情况下都没有关系。

标签: c++ berkeley-db


【解决方案1】:

您可能想查看 db_stat 实用程序提供的信息以及可用的 HASH 特定调整函数。请参阅BDB Reference Guide section on configuring a HASH database

我希望您在商用硬件上每秒获得成千上万次插入。您正在经历什么,您的绩效目标是什么?

问候,

戴夫

【讨论】:

    【解决方案2】:

    我建议尝试批量插入 API,您可以在此处的文档中阅读相关内容: http://www.oracle.com/technology/documentation/berkeley-db/db/api_reference/CXX/dbput.html#put_DB_MULTIPLE_KEY

    另外,我猜你对 memp_trickle 的调用是造成大部分减速的原因。随着缓存变得越来越脏,寻找要涓流的页面变得更加昂贵。实际上,由于您只是在写入,因此拥有大缓存只会带来伤害(一旦写入数据,您就不会再次使用它,因此您不希望它在缓存中徘徊。)我会推荐测试不同(较小)的缓存大小。

    最后,如果您只关心插入性能,则使用较大的页面大小会有所帮助。您将能够在每个页面上放置更多数据,从而减少磁盘写入。

    -本

    【讨论】:

      【解决方案3】:

      memp_trickle 几乎可以肯定会减慢速度。使用涓涓细流通常很好,但它属于自己的线程才能有效。 BDB(除非您进入更高级别的复制 API)不会为您创建线程——在幕后(线程方面)没有任何事情发生。当您从缓存中强制使用脏页时,trickle 将有效(查看统计输出以查看是否发生了这种情况)。

      您也可以考虑使用 BTREE 而不是 HASH。是的,我知道你特别提到了哈希,但为什么呢?如果您希望最大限度地提高性能,为什么要添加该限制?您可能能够利用参考的位置来减少缓存占用空间 - 您相信通常有更多的位置,或者您可以创建一些 - 如果您生成随机数字的密钥,例如,在日期前面和时间。这通常将局部性引入感知的“随机”系统。如果您确实使用 btree,则需要注意系统密钥的字节顺序(在 Wikipedia 中查找 Endianness),如果您使用的是 Little Endian 系统,则需要交换字节。使用具有正确顺序和引入位置的 BTREE 意味着您的键/值对将以“键生成时间”顺序存储,因此如果您看到最近键上的最多操作,您将倾向于点击相同的页面一遍又一遍(查看统计数据中的缓存命中率)。所以你需要更少的缓存。另一种思考方式是在缓存数量相同的情况下,您的解决方案将按更大的倍数进行扩展。

      我希望您的实际应用程序确实不会按顺序插入整数键(如果这样做,您会很幸运)。因此,您应该编写一个密切模拟您的访问模式的基准测试,至少在以下方面:密钥大小、数据大小、访问模式、数据库中的项目数、读/写混合。一旦你有了这些,看看统计数据——密切关注任何暗示 IO 或争用的东西。

      顺便说一句,我最近在http://libdb.wordpress.com 开设了一个博客,讨论 BDB 性能调优(以及其他与 BDB 相关的问题)。你可能会在那里得到一些好主意。根据您进行的调整类型,延迟和吞吐量可能会有很大差异。具体见http://libdb.wordpress.com/2011/01/31/revving-up-a-benchmark-from-626-to-74000-operations-per-second/

      【讨论】:

        【解决方案4】:

        您的性能下降可能有多种原因,这些原因实际上与您的代码无关。我可能弄错了,但我认为这都是关于内部数据库结构(并使用了数据结构)。

        考虑一种情况,其中数据库使用哈希表以外的其他方法,例如RB树。在 Big-O 意义上插入该树将占用O(logN),并且每个插入的元素都会增加 next 插入所需的时间。

        不幸的是,普通的哈希表也可能发生同样的情况,因此初始O(1) 插入操作时间会降低到更糟的程度。这可能有几个原因,但都是关于 散列冲突,这可能是由于错误的散列函数、错误的数据(即当前使用的散列函数 错误)甚至由于月相。

        如果我是你,我会尝试挖掘你的数据库内部结构。另外,我认为用你的数据库以外的东西(例如boost::unordered_map)测试你的密钥也可能有利于你的测试和分析。

        编辑:还要提一下,您是否尝试更改示例中的 cache_size 内容?或者可能还有其他一些与性能相关的参数可以修改?

        【讨论】:

        • 是的,我尝试操纵 cache_size 但它似乎对写入性能没有任何影响。
        猜你喜欢
        • 2011-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-16
        • 1970-01-01
        • 1970-01-01
        • 2020-12-30
        相关资源
        最近更新 更多