在我们的用例中,我们从我们的客户(大小约30GB)获得了包含数百万条记录的大型快照文本文件(tsv,csv等)。数据如下所示:
ItemId (unique), Title, Description, Price etc.
shoe-id1, "title1", "desc1", 10
book-id-2, "title2", "desc2", 5
每当我们从客户那里获得快照时,我们需要计算“delta”:
已插入 - 已插入的记录(仅显示在最新文件中,而不是前一个文件中),
已更新 - 相同ID但在任何其他列中的值不同
已删除(仅存在于上一个文件中,而不是最新文件中)。
(后续文件中的数据可能不正常,并且实际上并未对任何列进行排序)。
我们需要能够为不同的客户每天多次运行。
我们目前将所有数据从快照文件1存储到SQL服务器(12个分片(由customerId分区),总共包含十亿行),并在收到快照文件2时使用多个查询计算差异。事实证明这是非常低效的(小时,删除特别棘手)。我想知道是否有更快的解决方案。我对任何技术都开放(例如hadoop,nosql数据库)。关键是速度(最好是几分钟)。
答案 0 :(得分:1)
通常,判断id
是否出现在数据集中的最快方法是使用散列,因此我会创建一个使用id
作为密钥的哈希值和一个MD5校验和或CRC剩余的列作为存储在该键的元素。如果您的数据有很多列,这应该可以减轻内存压力。为什么我这么想?因为你说你有数百万条记录的GB数据,所以我推断每条记录必须是千字节的数量级 - 即相当宽。
所以,我可以在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 ++中重新完成,以进一步加快速度并进一步降低内存需求。