【问题标题】:Reading through a file line by line without loading whole file into memory逐行读取文件而不将整个文件加载到内存中
【发布时间】:2014-04-26 22:22:57
【问题描述】:

我正在使用 50 Gb MySQL 导出文件,并对其执行脚本操作列表以转换为 SQLite3 可加载表单(我从这里得到线索:script to convert mysql dump sql file into format that can be imported into sqlite3 db)。我没有研究 MySQL 转储的结构,数据是从第三方获得的。我可以看到它有创建表和插入语句,但考虑到它的大小,很难手动阅读和理解结构。 由于文件的大小,管道传输文件将不起作用。还有一个bash脚本来加载文件然后逐行处理,比如

while read line
<do something>

抱怨内存不足。

所以我尝试选择每一行,使用 awk 或 sed(两者都有效),将该行写入文件,然后将其传递给 perl 脚本列表。这是我正在使用的 awk 脚本

$ awk -vvar="$x" 'NR==var{print;exit}' file > temp

其中 x 保存行号,然后 temp 通过 perl 命令发送,最后附加到输出文件中。

然而,虽然一开始速度很快,但它很快就会变慢,因为它从一开始就必须迭代越来越多的行。大约有 40,000 行。

有人用过这样的东西吗?有更快的方法吗?

【问题讨论】:

  • 为什么脚本必须迭代越来越多的行?我猜你没有 40000 个文件,每个文件都包含一行,但你能提供更多细节吗?
  • 命令 $ sed -n '10{p;q}' file ,将读取前 9 行并给我第 10 行并退出 sed。随着数字上升到 40,000 ,它必须遍历更多行才能给我特定的行。
  • 好的,我明白了.. 是否可以先将文件拆分为 50 个 1Gb 文件?然后对每个块进行处理,最后再次加入块..
  • 我在超级计算机上可以拥有的文件数量受到限制,所以虽然 50 太多了,但我可以尝试获得比 1 更好的最大数量。感谢领导.
  • 也许您也可以发布您正在使用的脚本? (如果它很大,您可以尝试将其上传到 pastebin.com 并在此处提供链接)..

标签: mysql perl bash sqlite bigdata


【解决方案1】:

一次只处理一行:

while read -r line
do
    echo "$line" > temp
    …process temp with Perl, etc…
done < file

至少这不会表现出读取文件的二次行为,这是您的awk 脚本所做的。 它只读取一次大文件,这是 Big-O 表示法中的最佳性能(在常数因子内)。

如果如您所说,这会导致bash 出现问题,那么您应该使用 Perl 来读取每一行。在 50 GiB 的数据中有 40,000 行,每行大约有 1¼ MiB 的数据。这不太可能导致 Perl 出现任何问题,尽管它可能会导致 Bash 问题。您可以修改现有的 Perl 以一次读取一行,或者使用一个简单的包装 Perl 脚本来完成上述 Bash 脚本的工作。

wrapper.pl

假设你当前的处理脚本叫做script.pl

#!/usr/bin/env perl
use strict;
use warnings;

my $file = "temp";

while (<>)
{
    open my $fh, ">", $file or die;
    print $fh $_;
    close $fh;
    system "perl", "script.pl", $file;
}

未经测试的代码

调用:

perl wrapper.pl <file >output

【讨论】:

    猜你喜欢
    • 2015-08-07
    • 2016-11-03
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 2014-10-22
    • 1970-01-01
    相关资源
    最近更新 更多