【问题标题】:Perl: Trouble storing a huge hash on disk?Perl:在磁盘上存储巨大的哈希有问题吗?
【发布时间】:2014-02-14 01:44:56
【问题描述】:

我正在从事一个 Perl 项目,该项目涉及使用大约 1700 万个键构建散列。这太大了,无法存储在内存中(我的笔记本电脑的内存只能保存大约 1000 万个键)。我知道解决方案是将数据存储在磁盘上,但在实践中我无法执行此操作。这是我尝试过的:

DB_File

use strict;
use DB_File;
my $libfile = shift;
my %library;
tie %library, "DB_File", "$libfile";
for (my $a = 1; $a < 17000000; a++) {
    # Some code to generate key and value #
    $library{$key} = $value;
}

这给了我一个分段错误:循环中的 11 部分,原因我不明白。

伯克利数据库

use strict; 
use BerkeleyDB;
my $libfile = shift;
my $library = new BerkeleyDB::Hash
    -Filename => $libfile,
    -Flags => DB_CREATE;

for (my $a = 1; $a < 17000000; a++) {
    # Some code to generate key and value #
    $library->db_put($key, $value);
}

这对于前 1500 万个键似乎效果很好,但随后会显着减慢并最终在循环结束时完全冻结。我认为这不是内存问题。如果我将循环分成四部分,将它们放在四个单独的程序中,并按顺序运行它们(每次向数据库添加约 400 万条记录),前三个成功完成,但第四个在数据库大约有 15 条时挂起万把钥匙。所以看起来 BerkeleyDB 可能只能处理大约 1500 万个哈希键???

DBM::Deep

use strict; 
use DBM::Deep;
my $libfile = shift;
my $library = new DBM::Deep $libfile;

for (my $a = 1; $a < 17000000; a++) {
    # Some code to generate key and value #
    $library->put($key => $value);
}

从初步测试来看,这似乎工作正常,但它真的很慢:每千个键大约 5 秒,或者运行整个循环约 22 小时。如果可能的话,我宁愿避免这种情况。

我将非常感谢有关解决其中一个软件包的建议,或有关完成相同任务的其他选项的想法。

更新

【问题讨论】:

  • 看看使用 mongodb 之类的 nosql 数据库是否适合您。 mongodb.com/learn/nosql
  • 什么操作系统和 perl 版本以及您尝试过的模块?您的平均键和平均值有多大?
  • 你能在“key sorted”模式下生成条目吗?我想我记得一个巨大的 BerkeleyDB 案例,当从散列切换到 btree 时,“键排序”插入提高了性能。改进非常显着,但还不够。
  • @AndrzejA.Filip:使用 Btree 就可以了。谢谢!!!
  • 在报告的情况下,我记得巨大的 BerkeleyDB 创建时间从哈希的几个小时减少到带有“键排序”附加的 btree 的 20 分钟。反正时间太长了,所以这个家伙决定切换到能够直接访问 SQL 数据库的软件,以避免将 SQL 数据库“转储”到 BerkeleyDB。

标签: database perl hash berkeley-db tie


【解决方案1】:

切换到 btree 可以提高以“键排序模式”访问的 HUGE BerkeleyDB 的性能。它减少了所需的磁盘 I/O 操作次数。

案例研究: 在新闻报道的一个案例中:comp.mail.sendmail 我记得巨大的 BerkeleyDB 创建时间从哈希的几个小时减少到带有“键排序”附加的 btree 的 20 分钟。无论如何它太长了,所以这个家伙决定切换到能够直接访问 SQL 数据库的软件,从而避免将 SQL 数据库“转储”到 BerkeleyDB。 (virtusertable, sendmail->postfix)

【讨论】:

    【解决方案2】:

    你可以试试 PostgreSQL。

    先创建一个有两列key和value的表,varchar就可以了,

    然后,不要插入每一个,而是使用 Pg::BulkCopy 将数据复制到数据库。

    我建议一次插入不超过 100 MB,因为当您的 COPY 命令失败时,PostgreSQL 会将之前插入的那些行保留在磁盘上,并且只有在您 VACUUM FULL 表时才会将其删除。 (有一次我处理了很多 5GB,其中一些由于几乎结束的一些限制而失败,并且磁盘在回滚时永远不会恢复..)

    ps:你也可以直接使用 DBD::Pg:https://metacpan.org/pod/DBD::Pg#COPY-support

    所有复制完成后,您可以在 key 上创建索引,如果您需要更快的速度,请使用 Redis 或带有 MAXMEMORY POLICY 的 memcached

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-02
      • 2011-09-12
      • 2014-01-09
      • 2014-09-24
      • 1970-01-01
      • 1970-01-01
      • 2017-11-11
      相关资源
      最近更新 更多