打开读取文件太大的内存文件

时间:2014-07-09 17:48:13

标签: perl memory-management

我有一个简单的Perl脚本,它读取预期格式的文件,将一些相关信息保存到散列,使用散列执行某些操作,然后关闭文件并移动到下一个文件。

我需要考虑一个我的系统内存小于文件参数大小的场景,所以我想,为什么不试试呢?所以我将我的VirtualBox限制为1GB的RAM,创建了一个1.5GB的列表,并将其作为输入提供给它。没有错误被抛出,只是运行并运行。

我使用“free”命令查看了正在使用的系统内存,它显示了99%的内存正在使用,并且不会随着时间的推移而增加。我的Perl脚本中有一些中断处理代码,当我按下它时,我的vbox变得没有响应,我必须重新启动它。

我需要做的事情是输入几个格式为

的邮件列表
1-bob@bob.com

并将电子邮件发送到邮件列表中的每个电子邮件地址。我通过将它们添加到哈希来跟踪所有电子邮件地址。当每个地址被处理(即添加到哈希)时,我也会对其进行验证,如果它是好的,则会向它发送一封电子邮件。

有更好的方法来测试吗?我做错了吗?

use strict;
use warnings;

my %email_db;

while (<>) {
  chomp;
  my ($id, $email) = split /-/;
  push @{ $email_db{$id} }, $email;
}

预期的输入文件数量可以从1到5(我在这里假设 - 问题不是特定的),并且根据文件大小给出的示例是100MB系统内存和10GB文件大小。最后哈希中的条目数是未知的。

哈希的每个元素都有

key   = unique ID
value = array of email addresses for that ID

3 个答案:

答案 0 :(得分:2)

这是一个使用SQLite数据库而不是Perl哈希来存储电子邮件信息的解决方案。

我无法添加任何代码来发送电子邮件或报告群组计数,因为您还没有指定这些部分。

作为替代方案,您可能需要等到所有输入文件都被读取并插入数据库后再发送任何电子邮件。

如果从TEMPORARY表中删除emails属性,以便在程序完成时它不会消失,则可能更容易调试此代码。

use strict;
use warnings;

use DBI;

my $dbh = DBI->connect(
  'dbi:SQLite:dbname=email_db.sqlite', '', '',
  { RaiseError => 1, PrintError => 0 }
);

$dbh->do(<<END_SQL);
CREATE TEMPORARY TABLE emails (
  group_id INTEGER,
  email    TEXT,
  UNIQUE (group_id, email)
)
END_SQL

my $insert = $dbh->prepare(<<END_SQL);
INSERT INTO emails (group_id, email)
VALUES (?, ?)
END_SQL

$insert->{RaiseError} = 0;

while (<>) {
  next unless /-/;
  chomp;
  my ($group_id, $email) = split /-/;
  if ($insert->execute($group_id, $email)) {
    # Send email
  }
  else {
    my $errstr = $insert->errstr;
    die $errstr unless $errstr =~ /UNIQUE constraint failed/;
  }
}

my $sort = $dbh->prepare(<<END_SQL);
SELECT group_id, count(email)
FROM emails
GROUP BY group_id
ORDER BY count(email) DESC
END_SQL

$sort->execute;

while (my $row = $sort->fetchrow_arrayref) {
  printf "Group %d (%d emails)\n", @$row;
}

答案 1 :(得分:2)

为什么不使用SDBM哈希?它是链接到文件的哈希。这是一个&#39; lite&#39;解决方案,不需要安装完整的数据库。只是简单的Perl。密钥大小+数据必须小于1008字节。

use Fcntl; # Needed by SDBM_File
use SDBM_File;

# 'myemails' is the name of the file, and 2 files will be created: 
# myemails.pag and myemails.dir
tie(%h, 'myemails', 'filename', O_RDWR|O_CREAT, 0666) 
or die "Couldn't tie SDBM file 'filename': $!; aborting";
# Now read and change the hash
$h{'bob@somewhere.com'} = 1; # This email now exists in hash.
print $h{'bob@somewhere.com'}."\n";
...
untie %h;

我使用了500,000行文本文件,但脚本在我们的linux机器上运行。

此外,您可以在运行脚本时使用此文件,因此您不会每天/每周/每月向dupe电子邮件地址发送邮件。

要清除电子邮件,请将myemails.dir和myemails.pag移至备份文件,例如:myemails2014-07.dir和myemails-2014-07.pag。

编辑:任何人都知道SDBM中的密钥数量限制或SDBM文件的最大尺寸?这可以使用100万个电子邮件地址吗?我有一个带有10,000个密钥的SDBM文件。

答案 2 :(得分:0)

我想建议模块Tie::File::AsHashTie::File

我自己没有使用其中任何一个,所以没有任何经验可以分享。

相关问题