【问题标题】:Berkeleydb - B-Tree versus Hash TableBerkeleydb - B 树与哈希表
【发布时间】:2011-05-06 23:19:30
【问题描述】:

我试图了解在使用 BerkeleyDB 时应该驱动访问方法选择的因素:B-Tree 与 HashTable。 Hashtable 提供 O(1) 查找,但插入很昂贵(使用线性/可扩展散列,我们为插入分摊了 O(1))。但是 B 树提供 log N(base B)查找和插入时间。 B-Tree 还可以支持范围查询并允许按排序顺序访问。

  1. 除了这些考虑之外,还应考虑哪些因素?
  2. 如果我不需要支持范围查询,是否可以只使用 Hashtable 访问方法?

【问题讨论】:

    标签: hashtable berkeley-db b-tree


    【解决方案1】:

    对于许多应用程序,数据库是随机访问的,交互方式 或与“交易”。如果您有数据进入,这可能会发生 从网络服务器。但是,您通常必须填充大量 数据库一次,作为“批处理”操作。如果您可能会发生这种情况 正在进行数据分析项目,或将旧数据库迁移到 新格式。

    当您一次填充数据库时,B-Tree 或其他 排序索引更可取,因为它允许批量插入 更有效地完成。这是通过排序来实现的 将密钥放入数据库之前。填充 BerkeleyDB 具有 1000 万个条目的数据库可能需要一个小时 是未排序的,因为每次访问都是缓存未命中。但是当 条目被排序,同样的过程可能只需要十分钟。 连续键的接近意味着您将使用各种 缓存几乎所有的插入。排序可以做的很 速度快,因此整个操作可以加快数倍 通过在插入数据之前对数据进行排序。使用哈希表索引, 因为您事先不知道每个键旁边会出现哪些键 其他的,这个优化是不可能的。

    更新:我决定提供一个实际示例。它基于 以下脚本“db-test

    #!/usr/bin/perl
    use warnings;
    use strict;
    use BerkeleyDB;
    my %hash;
    unlink "test.db";
    tie %hash, (shift), -Filename=>"test.db", -Flags=>DB_CREATE or die;
    while(<>) { $hash{$_}=1; }
    untie %hash;
    

    我们可以使用包含 1600 万个条目的 Wikipedia 转储索引文件对其进行测试。 (我在 800MHz 2 核笔记本电脑上运行此程序,内存为 3G)

    $ >enw.tab bunzip2 <enwiki-20151102-pages-articles-multistream-index.txt.bz2
    $ wc -l enw.tab
    16050432 enw.tab
    $ du -shL enw.tab
    698M    enw.tab
    $ time shuf enw.tab > test-shuf
      16.05s user 6.65s system 67% cpu 33.604 total
    $ time sort enw.tab > test-sort
      70.99s user 10.77s system 114% cpu 1:11.47 total
    $ time ./db-test BerkeleyDB::Btree < test-shuf
      682.75s user 368.58s system 42% cpu 40:57.92 total
    $ du -sh test.db
    1.3G    test.db
    $ time ./db-test BerkeleyDB::Btree < test-sort
      378.10s user 10.55s system 91% cpu 7:03.34 total
    $ du -sh test.db
    923M    test.db
    $ time ./db-test BerkeleyDB::Hash < test-shuf
      672.21s user 387.18s system 39% cpu 44:11.73 total
    $ du -sh test.db
    1.1G    test.db
    $ time ./db-test BerkeleyDB::Hash < test-sort
      665.94s user 376.65s system 36% cpu 46:58.66 total
    $ du -sh test.db
    1.1G    test.db
    

    您可以看到对 Btree 键进行预排序会降低插入时间 从 41 分钟缩短到 7 分钟。排序只需要 1 分钟,所以 有很大的净收益 - 数据库创建速度提高了 5 倍。和 Hash 格式,创建时间同样慢 是否排序。另请注意,数据库文件大小较小 排序的插入;大概这与树平衡有关。

    加速一定是由于某种缓存,但我不确定 在哪里。很可能我们在内核的缓存未命中率较低 带有排序插入的页面缓存。这将与 CPU 使用率 - 当页面缓存未命中时,进程 从磁盘检索页面时必须等待,因此 CPU 使用率是 更低。

    为了比较,我还对两个较小的文件进行了相同的测试。

    File       | WP index         | Wikt. words       | /usr/share/dict/words
    Entries    | 16e6             | 4.7e6             | 1.2e5
    Size       | 700M             | 65M               | 1.1M
    shuf time  | 34s              | 4s                | 0.06s
    sort time  | 1:10s            | 6s                | 0.12s
    -------------------------------------------------------------------------
               | total  DB   CPU  |                   |
               | time  size  usage|                   |
    -------------------------------------------------------------------------
    Btree shuf | 41m,  1.3G, 42%  | 5:00s, 180M, 88%  | 6.4s, 3.9M, 86%
          sort | 7m,   920M, 91%  | 1:50s, 120M, 99%  | 2.9s, 2.6M, 97%
    Hash  shuf | 44m,  1.1G, 39%  | 5:30s, 129M, 87%  | 6.2s, 2.4M, 98%
          sort | 47m,  1.1G, 36%  | 5:30s, 129M, 86%  | 6.2s, 2.4M, 94%
    -------------------------------------------------------------------------
    Speedup    | 5x               | 2.7x              | 2.2x
    

    对于最大的数据集,排序插入为我们提供了 5 倍的加速。 用最小的,我们仍然得到 2 倍的加速 - 即使数据 很容易放入内存中,因此 CPU 使用率始终很高。这似乎 暗示我们正在从另一个效率来源中受益 除了页面缓存,5x 加速实际上是由于 与页面缓存和其他内容相等 - 也许更好 树平衡?

    无论如何,我更喜欢 Btree 格式,因为它允许 更快的批处理操作。即使最终数据库在 随机,我使用批处理操作进行开发、测试和 维护。如果我能找到加快这些速度的方法,生活会更轻松。

    【讨论】:

      【解决方案2】:

      引用this 中伯克利数据库的两位主要作者对架构的描述:

      Btree 和 Hash 访问方法的主要区别在于 Btree 为键提供参考位置,而 Hash 不提供。这 意味着 Btree 是几乎所有数据的正确访问方法 套;但是,哈希访问方法适用于数据集,因此 甚至 Btree 索引结构都无法装入内存。在 那一点,最好将内存用于数据而不是索引 结构。这种权衡在 1990 年更有意义,当时主要 内存通常比现在小得多。

      因此,也许在嵌入式设备和特殊用例中,哈希表可能会起作用。 BTree 用于现代文件系统,如Btrfs,它几乎是构建数据库或文件系统的理想数据结构。

      【讨论】:

        【解决方案3】:

        当您的数据集变得非常大时,B 树仍然会更好,因为大部分内部元数据可能仍然适合缓存。哈希,就其性质(数据的均匀随机分布)而言,本质上对缓存不友好。即,一旦数据集的总大小超过工作内存大小,哈希性能就会急剧下降,而 B-tree 性能会优雅地下降(实际上是对数)。

        【讨论】:

          【解决方案4】:

          这取决于您的数据集和键在小型数据集上,您的基准将接近相同,但是在较大的数据集上,它可能会根据键的类型/您拥有的数据量而有所不同。通常 b-tree 会更好,直到 btree 元数据超出您的缓存并最终执行大量 io,在这种情况下哈希会更好。此外,正如您所指出的,b-tree 插入更昂贵,如果您发现您将进行大量插入和少量读取,则哈希可能会更好,如果您发现您进行少量插入但大量读取,则 b-tree 可能变得更好。

          如果您真的关心性能,您可以尝试这两种方法并运行自己的基准 =]

          【讨论】:

            猜你喜欢
            • 2011-11-10
            • 1970-01-01
            • 2010-10-23
            • 2023-03-31
            • 2019-05-17
            • 2016-10-03
            • 2010-11-30
            • 1970-01-01
            • 2011-06-18
            相关资源
            最近更新 更多