【发布时间】:2017-01-26 08:58:58
【问题描述】:
我已将两个 ISO 文件合并为一个文件。两个单独的 ISO 文件都是同一供应商的 Linux 发行版,但版本不同。在我编写的程序中(如下所示),以 512 字节的块读取的连接文件和块的 MD5sum 被计算。 MD5sum 存储在Hashet<String> 中。如果使用HashSet 查找找到具有相同签名的块,则会记录下来。
在实际查找HashSet 之前,也使用BloomFilter 完成了完全相同的算法。由于BloomFilter 仅提供“非包含”保证并且可以提供包含错误肯定,如果BloomFilter 报告可能已经存在密钥,我也会查找 HashSet。
级联文件大小 > 1GB,因此 512 字节块签名的数量超过 177 万。使用BloomFilter 的方法的性能始终是第一种方法的约六倍。
为什么会出现这种情况?我在这里做错了吗?
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.time.StopWatch;
public class SimpleDedupTrial {
public static void main(String[] args) throws IOException {
int blockSize = 512;
HashSet<String> signatureSet = new HashSet<>();
File f = new File(
"D:\\keshav\\per\\projects\\work-area\\dedup-temp\\merged-iso"
);
FileInputStream fis = new FileInputStream(f);
long length = f.length();
long sizeSaved = 0l;
StopWatch sw = new StopWatch();
int len;
byte[] buffer = new byte[blockSize];
while ((len = fis.read(buffer)) != -1) {
String md5Hex = DigestUtils.md5Hex(buffer);
if (sw.isStopped()) {
sw.start();
}
if (sw.isSuspended()) {
sw.resume();
}
if (signatureSet.contains(md5Hex)) {
sizeSaved += len;
} else {
signatureSet.add(md5Hex);
}
sw.suspend();
}
sw.stop();
fis.close();
System.out.println("Time: "+sw.getTime(TimeUnit.MILLISECONDS));
System.out.println("File size in MB: "+convertToMB(length));
System.out.println("Size saved in MB: "+convertToMB(sizeSaved));
System.out.println("Signature set size: "+signatureSet.size());
System.out.println("Duplicate ratio: "+ ((double)sizeSaved * 100 / length));
System.out.println("With Blooom:");
useBloomFilter();
}
private static long convertToMB(long sizeInBytes) {
return sizeInBytes / (1024 * 1024);
}
private static void useBloomFilter() throws IOException {
int blockSize = 512;
Funnel<String> strFunnel = (String t, PrimitiveSink ps) -> {
ps.putString(t, Charsets.US_ASCII);
};
HashSet<String> signatureSet = new HashSet<>();
File f = new File(
"D:\\keshav\\per\\projects\\work-area\\dedup-temp\\merged-iso"
);
FileInputStream fis = new FileInputStream(f);
long length = f.length();
long sizeSaved = 0l;
BloomFilter<String> signatureBloomFilter = BloomFilter.create(
strFunnel, (length / blockSize)
);
StopWatch sw = new StopWatch();
int len;
byte[] buffer = new byte[blockSize];
while ((len = fis.read(buffer)) != -1) {
String md5Hex = DigestUtils.md5Hex(buffer);
if (sw.isStopped()) {
sw.start();
}
if (sw.isSuspended()) {
sw.resume();
}
if (signatureBloomFilter.mightContain(md5Hex)) {
if (!signatureSet.contains(md5Hex)) {
signatureBloomFilter.put(md5Hex);
signatureSet.add(md5Hex);
} else {
sizeSaved += len;
}
} else {
signatureBloomFilter.put(md5Hex);
signatureSet.add(md5Hex);
}
sw.suspend();
}
sw.stop();
fis.close();
System.out.println("Time: "+sw.getTime(TimeUnit.MILLISECONDS));
System.out.println("File size in MB: "+convertToMB(length));
System.out.println("Size saved in MB: "+convertToMB(sizeSaved));
System.out.println("Signature set size: "+signatureSet.size());
System.out.println("Duplicate ratio: "+ ((double)sizeSaved * 100 / length));
}
}
样本输出:
Time: 819
File size in MB: 1071
Size saved in MB: 205
Signature set size: 1774107
Duplicate ratio: 19.183032558071734
With Blooom:
Time: 4539
File size in MB: 1071
Size saved in MB: 205
Signature set size: 1774107
Duplicate ratio: 19.183032558071734
【问题讨论】:
-
对我来说,您误解了
BloomFilter的用途,BloomFilter是一种轻量级数据结构,我们在被称为像磁盘访问一样慢的东西之前使用它......并且被称为太大加载并简单地保存在缓存中的内存中。在这里,您已经以某种方式使用了缓存(您的 HashSet),如果您负担得起,则根本不需要BloomFilter。 -
我明白了。在我查找文件块签名的用例中,BloomFilters 并没有真正的帮助。是对的吗?假设数百万个文件块签名已经存储在磁盘上的单独文件中(预先计算),并且我只在内存中加载了相应的 BloomFilter。每当 BloomFilter 报告可能存在密钥时,我必须将所有数百万个签名加载到内存中以确定它。我在吗?
-
如果您需要将数百万个签名加载到内存中,则说明您的架构存在问题。您需要能够直接检查给定签名是否存在。假设您已将所有签名定义到数据库中,并且您使用签名作为主键。假设所有签名都无法放入您的内存中,那么您将使用 BloomFilter,您最初将在其中加载所有签名,然后不是启动查询来检查给定签名是否存在,而是首先检查您的 BF,如果它存在这将允许您减少查询的总数。
-
只有在 BF 表明它可能存在的情况下才进行检查,正确配置 BF 以限制误报的可能性可以大大减少对慢速后端的访问总量
标签: java algorithm hash md5sum bloom-filter