两个大文本文件的高效文件比较

时间:2017-10-12 03:26:14

标签: database performance bigdata large-files

在我们的用例中,我们从我们的客户(大小约30GB)获得了包含数百万条记录的大型快照文本文件(tsv,csv等)。数据如下所示:

ItemId (unique), Title, Description, Price etc.
shoe-id1, "title1", "desc1", 10
book-id-2, "title2", "desc2", 5

每当我们从客户那里获得快照时,我们需要计算“delta”:

  1. 已插入 - 已插入的记录(仅显示在最新文件中,而不是前一个文件中),

  2. 已更新 - 相同ID但在任何其他列中的值不同

  3. 已删除(仅存在于上一个文件中,而不是最新文件中)。

  4. (后续文件中的数据可能不正常,并且实际上并未对任何列进行排序)。

    我们需要能够为不同的客户每天多次运行。

    我们目前将所有数据从快照文件1存储到SQL服务器(12个分片(由customerId分区),总共包含十亿行),并在收到快照文件2时使用多个查询计算差异。事实证明这是非常低效的(小时,删除特别棘手)。我想知道是否有更快的解决方案。我对任何技术都开放(例如hadoop,nosql数据库)。关键是速度(最好是几分钟)。

1 个答案:

答案 0 :(得分:1)

通常,判断id是否出现在数据集中的最快方法是使用散列,因此我会创建一个使用id作为密钥的哈希值和一个MD5校验和或CRC剩余的列作为存储在该键的元素。如果您的数据有很多列,这应该可以减轻内存压力。为什么我这么想?因为你说你有数百万条记录的GB数据,所以我推断每条记录必须是千字节的数量级 - 即相当宽。

enter image description here

所以,我可以在Perl中合成一个13M旧值的哈希值和一个15M新值的哈希值,然后找到添加,更改和删除的值,如下所示。

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

# Set $verbose=1 for copious output
my $verbose=0;

my $million=1000000;
my $nOld=13*$million;
my $nNew=15*$million;

my %oldHash;
my %newHash;
my $key;
my $cksum;
my $i;
my $found;

print "Populating oldHash with $nOld entries\n";
for($i=1;$i<=$nOld;$i++){
   $key=$i-1;
   $cksum=int(rand(2));
   $oldHash{$key}=$cksum;
}

print "Populating newHash with $nNew entries\n";
$key=$million;
for($i=1;$i<=$nNew;$i++){
   $cksum=1;
   $newHash{$key}=$cksum;
   $key++;
}

print "Part 1: Finding new ids (present in newHash, not present in oldHash) ...\n";
$found=0;
for $key (keys %newHash) {
   if(!defined($oldHash{$key})){
      print "New id: $key, cksum=$newHash{rkey}\n" if $verbose;
      $found++;
   }
}
print "Total new: $found\n";

print "Part 2: Finding changed ids (present in both but cksum different) ...\n";
$found=0;
for $key (keys %oldHash) {
   if(defined($newHash{$key}) && ($oldHash{$key}!=$newHash{$key})){
      print "Changed id: $key, old cksum=$oldHash{$key}, new cksum=$newHash{$key}\n" if $verbose;
      $found++;
   }
}
print "Total changed: $found\n";

print "Part 3: Finding deleted ids (present in oldHash, but not present in newHash) ...\n";
$found=0;
for $key (keys %oldHash) {
   if(!defined($newHash{$key})){
      print "Deleted id: $key, cksum=$oldHash{$key}\n" if $verbose;
      $found++;
   }
}
print "Total deleted: $found\n";

在我的iMac上运行需要53秒。

./hashes 
Populating oldHash with 13000000 entries
Populating newHash with 15000000 entries
Part 1: Finding new ids (present in newHash, not present in oldHash) ...
Total new: 3000000
Part 2: Finding changed ids (present in both but cksum different) ...
Total changed: 6000913
Part 3: Finding deleted ids (present in oldHash, but not present in newHash) ...
Total deleted: 1000000

出于测试的目的,我在oldHash中创建了从0​​..12,999,999运行的密钥,newHash中的密钥从1,000,000..16,000,000运行,然后我可以轻松地告诉它是否有效新密钥应为13,000,000..16,000,000,删除的密钥应为0..999,999。我还使checksums在0和1之间交替,以便50%的重叠ID应该看起来不同。

以相对简单的方式完成后,我现在可以看到您只需要校验和部分来查找更改的ID,因此您可以执行第1部分和第3部分而不使用校验和来节省内存。在加载数据时,您也可以一次执行第2部分元素,这样您就不需要预先将所有旧ID和所有新ID加载到内存中。相反,您将加载较旧的旧数据集和新数据集,然后在将另一组ID读入内存时一次检查一个id的更改,这会降低对内存的要求。

最后,如果该方法有效,可以很容易地在C ++中重新完成,以进一步加快速度并进一步降低内存需求。