Berkeleydb - B树与哈希表

时间:2010-11-09 00:12:55

标签: hashtable berkeley-db b-tree

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

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

4 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

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

当您同时填充数据库时,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使用率始终很高。这似乎 意味着我们正从另一个效率来源中受益 除了页面缓存之外,还有5倍的加速实际到期 在页面缓存和其他东西的相同部分 - 也许更好 树平衡?

无论如何,我倾向于选择Btree格式,因为它允许 更快的批处理操作即使访问最终数据库也是如此 随机,我使用批处理操作进行开发,测试和 保养。如果我能找到加快这些速度的方法,生活会更容易。

答案 3 :(得分:0)

引用Berkeley DB的两位主要作者this撰写该架构:

  

Btree和Hash访问方法的主要区别在于   Btree为键提供了参考位置,而Hash则没有。这个   暗示Btree是几乎所有数据的正确访问方法   套;但是,Hash访问方法适用于数据集   即使是Btree索引结构也不适合内存。在   在这一点上,将内存用于数据比使用索引更好   结构。这种权衡在1990年主要时更有意义   记忆力通常比今天小很多。

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