Perl:麻烦在磁盘上存储一个巨大的哈希?

时间:2014-02-14 01:44:56

标签: database perl hash berkeley-db tie

我正在开发一个涉及使用大约1700万个密钥构建哈希的Perl项目。这太大了,无法存储在内存中(我的笔记本电脑的内存只能容纳大约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个部分,原因我不明白。

的BerkeleyDB

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 ::深

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个小时。如果可能的话,我宁愿避免这种情况。

我非常感谢有关对其中一个软件包进行故障排除的建议,或者有关完成相同操作的其他选项的建议。

更新

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

你可以试试PostgreSQL。

首先创建一个包含两列,键和值的表,varchar就可以了,

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

我建议一次插入不超过100 MB,因为当你的COPY命令失败时,PostgreSQL会保留那些在插入磁盘之前的那些行,并且只有在VACUUM FULL表的情况下它才会删除它。 (有一次我处理了大量的5GB,其中一些在几乎结束的某些约束上失败,磁盘再也没有回滚了。)

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

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