Perl:在磁盘上存储大型哈希表遇到问题?

3

我正在开发一个涉及构建包含约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;
}

这个循环在执行到一半时出现了“segmentation fault: 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万个记录),前三个程序能够成功完成,但当数据库有约1500万个键时第四个程序会挂起。因此看起来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数据库对你来说是一个可能的选择。http://www.mongodb.com/learn/nosql - stevenl
你尝试的操作系统、Perl版本和模块版本是什么?你的平均键和平均值有多大? - ysth
1
你能以“键排序”模式生成条目吗?我记得有一个巨大的BerkeleyDB案例,当从哈希切换到btree并使用“键排序”插入时,性能得到了提高。这种改进非常显著,但还不够。 - AnFi
@AndrzejA.Filip:使用B树解决了问题。谢谢!!! - dannyhmg
在报告的案例中,我记得HUGE BerkeleyDB的创建时间从哈希需要几个小时降至20分钟的btree,并带有“按键排序”的追加操作。无论如何这个时间仍然太长,所以那个人决定切换到软件能够直接访问SQL数据库,避免了需要将SQL数据库“转储”到BerkeleyDB的需求。 - AnFi
2个回答

2

使用btree可能会提高在“键排序模式”下访问的HUGE BerkeleyDB的性能。它减少了所需的磁盘I / O操作次数。

案例研究: 在一个报道于news:comp.mail.sendmail的案例中,我记得HUGE BerkeleyDB的创建时间从哈希方式几个小时降至btree方式20分钟,并且进行了“键排序”操作。但不管怎样,时间仍太长,所以这位先生决定转而使用软件来直接访问SQL数据库,避免了需要将SQL数据库“转储”到BerkeleyDB中的需要。(virtusertable,sendmail→postfix)


0

你可以尝试使用PostgreSQL。

首先,创建一个具有两列(key和value)的表,varchar类型将很好地满足需求,

然后,不要逐个插入数据,而是使用Pg::BulkCopy将数据批量复制到数据库中。

我建议每次插入不超过100MB,因为当COPY命令失败时,PostgreSQL会将已插入磁盘的行保留,并且只有在对表进行VACUUM FULL操作后才能删除它们。(我曾经处理了大量5GB的数据,最后几乎失败,磁盘无法回滚...)

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

在所有复制完成之后,你可以在key上创建索引,如果你需要更快的速度,可以使用Redis或者memcached,并设置MAXMEMORY POLICY。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接