我有一个应用程序,它将基于文件的数据存储在NTFS目录路径下,该路径关闭数据的SHA-1哈希。它有几个非常好的属性(重复数据删除,不受其他元数据更改等)但我很好奇人们在创建基于散列的目录存储结构时遇到的最佳实践。我主要关注的是文件/文件夹的数量,它们可以在给定的文件夹深度上实际存储。
有谁知道我会遇到什么样的限制?如果我将它们全部转储到存储路径根目录的文件夹中,我觉得我会严重限制存储增长的能力。虽然它不会很快成为一个问题,但我宁愿有一个避免这种情况的结构,而不是试图稍后重组大量存储。
如果我采用一种方法来填充签名以创建更深的树,是否有任何指导我需要多少才能将其分块?这样的事情就足够了吗?
StringBuilder foo = new StringBuilder(60);
// ...root, etc.
// SHA-1 always has a length of 40, chunk it up to distribute into smaller groups
// "\0000\0000000000000000\00000000000000000000"
foo.Append(Path.DirectorySeparatorChar);
foo.Append(sha1, 0, 4);
foo.Append(Path.DirectorySeparatorChar);
foo.Append(sha1, 4, 16);
foo.Append(Path.DirectorySeparatorChar);
foo.Append(sha1, 20, 20);
知道SHA-1具有相当不错的分布,我不得不假设最终会有大型集群,但平均而言它将是均匀分布的。我关心的就是这些集群。
访问太宽的目录结构时是否存在性能损失?我知道Windows资源管理器会阻塞,但是通过C#/ System.IO以编程方式访问呢?
答案 0 :(得分:3)
一些观察结果:
关于可以处理的深度目录的问题是好的 - 我无法回答它。但是你应该看一下,如果20个嵌套目录太难处理,因为20个级别允许你每个级别最多保留256个条目:
xx/xx/xx/xx/xx/...
另一方面,你可以坚持使用你的4个字符,这将导致最多10个和65536个条目的深度:
xxxx/xxxx/xxxx/xxxx/xxxx/...
然而 - 在这两种情况下,我都可能会编写一个动态算法,它会检查每个级别的项目数,并根据需要引入新的子文件夹。所以前256个(或65536个)项目只会转到一个目录。
答案 1 :(得分:1)
添加碰撞检测器和旋转变压器。如果有人试图检查SHA-1碰撞向量,你最好做好准备。
我还没有看到任何SHA-1碰撞,但我确实看到了一个意外的MD5碰撞的坏情况,有人认为它们是独一无二的。
无论如何,NTFS使用BTree目录结构,所以你真的可以将所有文件放在一个文件夹中。 Windows资源管理器不会喜欢它。
答案 2 :(得分:1)
感谢其他回答者的见解。
这听起来像是来自网络that NTFS can handle the sizes的其他问题,但Windows资源管理器和网络操作可能会在更低的阈值下窒息。我运行了一个非常均匀随机分布的模拟,类似于SHA-1为随机的1,000,000个“文件”生成的内容。
Windows资源管理器肯定不喜欢目录宽度为4,因为它很快接近该级别的最大值(65536)。我将前两个目录长度调整为每个3(最多4096),并将剩余的34个数字放在第三级,以尝试平衡深度与每个级别太多目录的概率。这似乎允许Windows资源管理器处理浏览结构。
这是我的模拟:
const string Root = @"C:\_Sha1Buckets";
using (TextWriter writer = File.CreateText(@"C:\_Sha1Buckets.txt"))
{
// simulate a very even distribution like SHA-1 would produce
RandomNumberGenerator rand = RandomNumberGenerator.Create();
byte[] sha1 = new byte[20];
Stopwatch watch = Stopwatch.StartNew();
for (int i=0; i<1000000; i++)
{
// populate bytes with a fake SHA-1
rand.GetBytes(sha1);
// format bytes into hex string
string hash = FormatBytes(sha1);
// C:\_Sha1Buckets
StringBuilder builder = new StringBuilder(Root, 60);
// \012\345\6789abcdef0123456789abcdef01234567\
builder.Append(Path.DirectorySeparatorChar);
builder.Append(hash, 0, 3);
builder.Append(Path.DirectorySeparatorChar);
builder.Append(hash, 3, 3);
builder.Append(Path.DirectorySeparatorChar);
builder.Append(hash, 6, 34);
builder.Append(Path.DirectorySeparatorChar);
Directory.CreateDirectory(builder.ToString());
if (i % 5000 == 0)
{
// write out timings every five thousand files to see if changes
writer.WriteLine("{0}: {1}", i, watch.Elapsed);
Console.WriteLine("{0}: {1}", i, watch.Elapsed);
watch.Reset();
watch.Start();
}
}
watch.Reset();
Console.WriteLine("Press any key to delete the directory structure...");
Console.ReadLine();
watch.Start();
Directory.Delete(Root, true);
writer.WriteLine("Delete took {0}", watch.Elapsed);
Console.WriteLine("Delete took {0}", watch.Elapsed);
}
在大约五万之后,模拟看起来有点慢(每5000秒15-20秒),但保持该速率。最后删除我的机器超过30分钟!
对于100万个哈希,分布就像这样:
这在Windows资源管理器中非常易于管理,并且似乎没有太深或太宽。显然,如果分布不均匀,那么我们可能遇到问题,但仅在第三级。前两个级别的界限为4096.我认为如果目标集更大,我们可以增加额外的水平并获得很大的增长潜力。对于我的申请,100万是一个非常合理的上限。
对于确定目录结构启发式的这种测试的有效性,是否有任何想法?