2017-10-12 73 views
0

在我們的使用案例中,我們從我們的客戶(大小約30GB)獲得大量快照文本文件(tsv,csv等)以及數百萬條記錄。數據是這樣的:兩個大文本文件的高效文件比較

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

無論何時,我們從客戶得到的快照,我們需要計算一個「增量」:

  1. 插入 - 插入的記錄(只存在於最新的文件,而不是以前的一個),

  2. 更新 - 在任何其他列

  3. 刪除(只存在於PR的ID相同,但不同的價值這個文件並不是最新的)。

(數據可能在後續文件中沒有順序,並且沒有在任何列上真正排序)。

我們需要能夠爲不同的客戶每天運行多次。

我們當前將所有來自快照文件1的數據存儲到SQL服務器(12個分片(由customerId分區),總共包含10億行),並在收到快照文件2時使用多個查詢計算差異。這被證明是非常低效的(小時,刪除特別棘手)。我想知道是否有更快的解決方案。我願意接受任何技術(例如hadoop,nosql數據庫)。關鍵是速度(最好是分鐘)。

+0

我正在考慮將獨特的id讀入兩個Perl哈希中 - 一個用於舊的舊哈希,另一個可能是每個記錄的剩餘字段的CRC/SHA校驗和作爲存儲在哈希中的項目。檢查通用/獨特的會員資格應該非常快。嘗試添加一個Perl標籤也許。 –

+0

你提到過關於文件大小。我可以知道速度是多少?意思是,你多久會得到這個後續文件。 –

+0

每天約20K次 – user5121292

回答

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"; 

這需要53秒在我的iMac上運行。

./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 

出於測試的目的,我從0..12,999,999在oldHash運行鍵和按鍵在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是否更改,這會降低對內存的要求。最後,如果這種方法有效,它可以很容易地在C++中重新完成,例如,進一步加快速度並進一步減少內存需求。