如何过滤非常非常大的文件

时间:2014-05-09 22:12:14

标签: algorithm sorting out-of-memory uniqueidentifier

我有一个非常大的未分类文件,1000GB,ID对

  1. ID:ABC123 ID:ABC124
  2. ID:ABC123 ID:ABC124
  3. ID:ABC123 ID:ABA122
  4. ID:ABC124 ID:ABC123
  5. ID:ABC124 ID:ABC126
  6. 我想过滤

    文件

    1)重复

    example
    ABC123 ABC124
    ABC123 ABC124
    

    2)反向对(丢弃第二次出现)

    example
    ABC123 ABC124
    ABC124 ABC123
    

    过滤后,上面的示例文件看起来像

    1. ID:ABC123 ID:ABC124
    2. ID:ABC123 ID:ABA122
    3. ID:ABC124 ID:ABC126
    4. 目前,我的解决方案是

      my %hash;
      
      while(my $line = <FH>){
           chomp $line; #remove \n
           my ($id1,$id2) = split / /, $line;
           if(exists $hash{$id1$1d2} || exists $hash{$id2$id1}){
                  next;
           }
           else{
               $hash{$id1$id2} = undef ; ## store it in a hash
               print "$line\n";
            }
      }
      

      这给了我较小列表的预期结果,但是对于较大的列表占用了太多内存,因为我将哈希存储在内存中。

      我正在寻找一种可以减少内存实施的解决方案。 我的一些想法是

      1)将哈希保存到文件而不是内存

      2)多次传递文件

      3)使用unix sort -u -k1,2

      对文件进行排序和统一

      在堆栈交换cs上发布后,他们建议使用外部排序算法

6 个答案:

答案 0 :(得分:3)

您可以使用map reduce执行任务。

Map-Reduce是一个批处理框架,允许您轻松地在多台计算机之间分配您的工作,并使用并行处理而无需考虑同步和容错。

map(id1,id2):
    if id1<id2:
        yield(id1,id2)
   else:
        yield(id2,id1)

reduce(id1,list<ids>):
   ids = hashset(ids) //fairly small per id
   for each id2 in ids:
       yield(id1,id2)

map-reduce实现将允许您在几台机器上分配您的工作,只需要很少的额外编程工作。
该算法还需要对数据进行线性(且相当小)的遍历,并且需要相当少量的额外内存,假设每个ID与少量其他ID相关联。

请注意,这将改变对的顺序(在某些情况下,使第一个id为秒)
如果原始ID的顺序很重要,您可以通过额外的字段轻松解决它。
另请注意,数据的顺序会发生变化,使用map-reduce时无法克服它。

为了提高效率,您可能需要添加一个组合器,在这种情况下它将执行与reducer相同的工作,但是如果它实际上有帮助,那么很大程度上取决于数据。

Hadoop是一个实现Map-Reduce的开源库,在社区中得到广泛使用。

答案 1 :(得分:3)

根据您的数据的详细信息(请参阅我对该问题的评论),Bloom filter可能是一种简单的方法,可以通过两次传球。在第一遍中,在排序第一个和第二个值之后将每一对插入过滤器并生成一组可能的重复项。在第二次传递中使用可能重复的集合过滤文件。这显然要求(可能的)重复集合本身不大。

鉴于数据集的特性 - 最多约250亿个唯一对,每对大约64位 - 结果将大约为200 GB。所以你需要大量的内存,许多通行证或许多机器。即使是Bloom过滤器也必须是巨大的才能产生可​​接受的错误率。

sortbenchmark.org可以提供有关所需内容的一些提示,因为任务与排序不同。 2011年获胜者使用了66个节点,其中包括2个四核处理器,24个GiB内存和16个500 GB磁盘,并在59.2秒内分类为1,353 GB。

答案 2 :(得分:3)

作为滚动自己聪明的解决方案的替代方法,您可以将数据添加到数据库中,然后使用SQL获取所需的子集。许多伟大的头脑已经解决了查询大数据的问题,1000GB并不是 大,所有事情都考虑在内......

答案 3 :(得分:1)

你的方法几乎没问题,你只需要将哈希值移动到磁盘而不是将它们保存在内存中。但是,让我们一步一步走。

重新订购ID

使用具有不同ID顺序的记录是不方便的。因此,如果可能,请重新排序ID,否则,为每个包含正确顺序的记录创建其他密钥。我假设你可以重新排序ID(我在Bash中不是很好,所以我的代码将在Python中):

with open('input.txt') as file_in, open('reordered.txt', 'w') as file_out:
    for line in file_in:
        reordered = ' '.join(sorted(line.split(' ')))  # reorder IDs
        file_out.write(reordered + '\n')

按哈希分组记录

您无法一次过滤所有记录,但可以将它们拆分为合理数量的部分。每个部分可以通过其中的记录的散列来唯一地标识,例如:

N_PARTS = 1000
with open('reordered.txt') as file_in:
    for line in file_in: 
        part_id = hash(line) % N_PARTS # part_id will be between 0 and (N_PARTS-1)
        with open('part-%8d.txt' % part_id, 'a') as part_file:
            part_file.write(line + '\n')

选择有功能在这里很重要。我使用了标准的Python hash()(模块N_PARTS),但您可能需要使用另一个函数,它会分配每个散列接近uniform的记录数。如果散列函数或多或少工作正常,而不是1Tb的1个大文件,你将获得1000个~100Mb的小文件。最重要的是,您可以保证不同部分中没有相同的记录。

请注意,打开和关闭每行的部分文件并不是一个好主意,因为它会产生无数的系统调用。事实上,更好的方法是保持文件打开(你可能需要增加你的ulimit -f),使用批处理甚至写入数据库 - 这取决于实现,而我将保持代码简单的目的示范。

过滤每个小组

100Mb文件更容易使用,不是吗?您可以将它们加载到内存中,并使用哈希集轻松删除重复项:

unique = set([])
for i in range(N_PARTS):                          # for each part
    with open('part-%8d.txt') as part_file: 
        file line in part_file:                   # for each line
            unique.add(line)
with open('output.txt', 'w') as file_out:
    for record in unique:
        file_out.write(record + '\n')

这种方法使用了一些繁重的I / O操作和3次传递,但它在时间上是线性的并且使用可配置的内存量(如果你的部件对于单个机器来说仍然太大,只需增加N_PARTS)。

答案 4 :(得分:1)

所以,如果这是我的话,我会采用@Tom所描述的数据库路径作为另一个答案。我在这里使用Transact SQL,但似乎大多数主要的SQL数据库都有类似的窗口/排名row_number()实现(MySQL除外)。

我可能会运行两种扫描方法,首先将id1id2列重写为新表,以使“最低”值在id1中并且在id2中最高{1}}。

这意味着后续任务是在此重写表中找到欺骗。

最初,您需要将源数据批量复制到数据库中,或生成一大堆insert语句。我已经去了这里的插入,但是赞成大数据的批量插入。不同的数据库有不同的方法来做同样的事情。

CREATE TABLE #TestTable
(
    id int,
    id1 char(6) NOT NULL,
    id2 char(6) NOT NULL
)

insert into 
#TestTable (id, id1, id2) 
values 
    (1, 'ABC123', 'ABC124'),
    (2, 'ABC123', 'ABC124'),
    (3, 'ABC123', 'ABA122'),
    (4, 'ABC124', 'ABC123'),
    (5, 'ABC124', 'ABC126');

select 
    id, 
    (case when id1 <= id2 
        then id1 
        else id2 
    end) id1,
    (case when id1 <= id2 
        then id2 
        else id1 
    end) id2
    into #correctedTable 
from #TestTable

create index idx_id1_id2 on #correctedTable (id1, id2, id)

;with ranked as
(select 
    ROW_NUMBER() over (partition by id1, id2 order by id) dupeRank, 
    id,
    id1,
    id2
 from #correctedTable)

select id, id1, id2 
  from ranked where dupeRank = 1

drop table #correctedTable
drop table #TestTable

这给了我们结果:

3           ABA122 ABC123
1           ABC123 ABC124
5           ABC124 ABC126

答案 5 :(得分:0)

我没有试图回答这个问题,只是将我的0.02欧元加到其他答案中。

对我来说,必须要做的就是将任务拆分为已经建议的多个较小的任务。控制流和数据结构。

Merge Sort was used with Tape Drives对大数据量进行排序的方式(大于内存,大于随机访问磁盘)。现在,这意味着存储分布在多个(联网)磁盘或网络磁盘扇区上。

已经有语言甚至操作系统以不同的粒度支持这种分发。大约10年前,我有这些任务的热门候选人,但我不记得那时的名字和事情发生了变化。

首先是分布式Linda Operating System,并根据需要连接/断开并行处理器。基本协调结构是巨大的分布式Tuple Space数据结构,其中处理器读/写任务并写入结果。

Multi agent systems([{3}}可能包含更多链接)Czech Wikipedia article更近期有类似工作分配的方法

相关维基百科文章为Parallel ComputingSupercomputer Operating SystemsList of concurrent and parallel programming languages

我并不是说你应该在超级计算机上购买一些处理器时间并在那里运行计算。我将它们列为要学习的算法概念。

因为很多时候会有一些免费或开源的软件解决方案可以让你在小型软件中做同样的事情。从便宜的软件和可用的硬件开始。例如1990年回到大学时,我们用计算机实验室的夜晚来计算ray-traced 3D images。这是一个计算成本非常高的过程,因为每个像素都必须投射一个&#34; ray&#34;并计算其与场景模型的碰撞。在具有一些眼镜和镜子的场景的1台机器上,它每秒运行1像素(C ++和优化的汇编语言代码)。在实验室里,我们有大约15台PC可用。因此最终时间可能减少约15倍(I386,I486和320x200 256色的图像)。图像被拆分为独立任务,并行计算并合并为一个。这种方法在当时很好,今天类似的方法对你也有帮助。

总是会有并且总是会出现大数据&#34;这么大,它不适合RAM并且它不适合磁盘而且无法在1台计算机上计算在有限的时间内。

从计算的最初几天起,这些任务就成功解决了。像B-Tree,磁带机,寻求时间,Fortran,Cobol,IBM AS / 400这样的术语来自那个时代。如果你喜欢那些时代的工程师,那么你肯定能拿出聪明的东西:)

编辑:实际上,您可能正在寻找External Sorting