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

Algorithm 如何过滤非常非常大的文件,algorithm,sorting,out-of-memory,uniqueidentifier,Algorithm,Sorting,Out Of Memory,Uniqueidentifier,我有一个非常大的未排序文件,1000GB,ID对 ID:ABC123 ID:ABC124 ID:ABC123 ID:ABC124 ID:ABC123 ID:ABA122 ID:ABC124 ID:ABC123 ID:ABC124 ID:ABC126 我想筛选文件中的 1) 复制品 example ABC123 ABC124 ABC123 ABC124 2) 反向对(放弃第二个引用) 过滤后,上面的示例文件如下所示 ID:ABC123 ID:ABC124 ID:ABC123 ID:ABA122

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

  • ID:ABC123 ID:ABC124
  • ID:ABC123 ID:ABC124
  • ID:ABC123 ID:ABA122
  • ID:ABC124 ID:ABC123
  • ID:ABC124 ID:ABC126
  • 我想筛选文件中的

    1) 复制品

    example
    ABC123 ABC124
    ABC123 ABC124
    
    2) 反向对(放弃第二个引用)

    过滤后,上面的示例文件如下所示

  • ID:ABC123 ID:ABC124
  • ID:ABC123 ID:ABA122
  • ID:ABC124 ID:ABC126
  • 目前,我的解决方案是

    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";
          }
    }
    
    my%hash;
    while(我的$line=){
    chomp$line;#删除\n
    my($id1,$id2)=拆分/,$line;
    if(exists$hash{$id1$1d2}| | exists$hash{$id2$id1}){
    下一个
    }
    否则{
    $hash{$id1$id2}=undef;##将其存储在哈希中
    打印“$line\n”;
    }
    }
    
    这为较小的列表提供了所需的结果,但对较大的列表占用了太多内存,因为我正在内存中存储哈希

    我正在寻找一个解决方案,将采取更少的内存来实现。 我的一些想法是

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

    2) 对文件进行多次传递

    3) 使用unix对文件进行排序和唯一性排序
    sort-u-k1,2

    在stack exchange cs上发布后,他们建议使用一种外部排序算法来执行任务

    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(id1,id2):
    
    如果id1取决于你数据的细节(见我对问题的评论),a可能是一个简单的方法,可以通过两次传球来逃脱。在第一个过程中,在对第一个值和第二个值进行排序后,将每一对插入到过滤器中,并生成一组可能的重复项。在第二遍中,使用一组可能的重复项筛选文件。这显然要求(可能的)副本集本身不是很大的

    考虑到数据集的特点(大约250亿个唯一对,每对大约64位),结果大约为200 GB。所以你要么需要大量的内存,多次传递,要么需要许多机器。即使是布卢姆过滤器也必须很大,才能产生可接受的错误率


    可以提供一些关于所需内容的提示,因为任务与排序没有区别。2011年的获奖者使用了66个节点,每个节点有2个四核处理器、24个GiB内存和16个500 GB磁盘,并在59.2秒内完成了1353 GB的排序。

    除了使用自己的智能解决方案外,您还可以将数据添加到数据库中,然后使用SQL获得所需的子集。许多伟人已经解决了查询“大数据”的问题,而1000GB实际上并没有那么大,从各方面考虑……

    您的方法几乎不错,您只需要将哈希值移动到磁盘上,而不是将其保存在内存中。但是让我们一步一步走

    重新排序ID

    处理ID顺序不同的记录很不方便。因此,如果可能的话,对ID重新排序,或者,如果没有,则为保存正确顺序的每个记录创建额外的键。我假设您可以对ID进行重新排序(我对Bash不是很在行,所以我的代码将使用Python):

    按哈希对记录进行分组

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

    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')
    
    has函数的选择在这里很重要。我使用了标准Python的
    hash()
    (模块N\u部分),但您可能需要使用另一个函数,该函数提供了记录数的分布,每个散列都接近。如果哈希函数的工作或多或少正常,那么您将得到1000个~100Mb的小文件,而不是1个1Tb的大文件。最重要的是你要保证在不同的地方没有两条相同的记录

    请注意,打开和关闭每一行的零件文件并不是一个好主意,因为它会生成无数的系统调用。事实上,更好的方法是保持文件打开(您可能需要增加
    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\u部件

    我不是想回答这个问题,只是在其他答案上加上我的0.02欧元

    我必须要做的一件事是将任务分割成多个较小的任务,正如已经建议的那样。控制流和数据结构

    排序大数据量(大于内存,大于随机访问磁盘)的方式。现在,这意味着存储是跨多个(网络)磁盘或网络磁盘扇区分布的

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

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

    与工作分布类似的最新方法是(可能包含更多链接)

    维基百科的相关文章有,和

    我不是说你应该在超级计算机上购买一些处理器时间并在那里运行计算。我
    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')
    
    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')
    
    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