基于SHA-1的目录结构和NTFS限制?

时间:2009-12-14 16:49:53

标签: c# ntfs sha1

我有一个应用程序,它将基于文件的数据存储在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以编程方式访问呢?

3 个答案:

答案 0 :(得分:3)

一些观察结果:

  • 你在4到10个字符后分裂。 4个字符本身可以导致一个目录中的65536个条目,10个字符将导致16 ^ 10个条目,这肯定是太多了(并且还有更多的特征......)
  • 所以下一个问题是:你是如何选择这个数字的?他们看起来像 magic 数字。您似乎希望您的分裂将在所有情况下完成工作......

关于可以处理的深度目录的问题是好的 - 我无法回答它。但是你应该看一下,如果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万个哈希,分布就像这样:

  • 第一级有4096个文件夹
  • 第二级平均有250个文件夹
  • 第3级平均有1个文件夹

这在Windows资源管理器中非常易于管理,并且似乎没有太深或太宽。显然,如果分布不均匀,那么我们可能遇到问题,但仅在第三级。前两个级别的界限为4096.我认为如果目标集更大,我们可以增加额外的水平并获得很大的增长潜力。对于我的申请,100万是一个非常合理的上限。

对于确定目录结构启发式的这种测试的有效性,是否有任何想法?