为什么Git .git / objects /文件夹又细分为许多SHA前缀文件夹?


21

Git在内部将对象(斑点,树)存储在.git/objects/文件夹中。每个对象都可以由从对象内容计算得出的SHA1哈希引用。

但是,对象并不.git/objects/直接存储在文件夹中。而是,每个对象都存储在一个文件夹中,该文件夹以其SHA1哈希的前缀开头。因此带有哈希值的对象b7e23ec29af22b0b4e41da31e868d57226121c84将存储在.git/objects/b7/e23ec29af22b0b4e41da31e868d57226121c84

为什么Git用这种方式细分其对象存储?

我可以找到的资源(例如git-scm 上有关Git内部的页面)仅说明了方法,而不是原因

Answers:


33

可以将所有文件放在一个目录中,尽管有时会变得有些大。 许多文件系统都有限制。您想将git存储库放在USB记忆棒上的FAT32格式的驱动器上吗?您只能在一个目录中存储65,535个文件。这意味着必须细分目录结构,以便不太可能填充单个目录。

对于其他文件系统和更大的git存储库,这甚至会成为问题。我已经闲逛了一个相对较小的git repo(大约360MiB),它有181,546个用于11k文件的对象。拉Linux仓库,您有4,374,054个对象。如果将所有这些文件放在一个目录中,则将无法检出文件,并且将导致文件系统崩溃(出于“ crash”的某种含义)。

所以?您将其按字节分割。FireFox等应用程序也采用了类似的方法:

~/Li/Ca/Fi/Pr/7a/Cache $ ls
0/           4/           8/           C/           _CACHE_001_
1/           5/           9/           D/           _CACHE_002_
2/           6/           A/           E/           _CACHE_003_
3/           7/           B/           F/           _CACHE_MAP_

除此之外,它还涉及性能问题。考虑具有多个长文件名的NTFS性能

Windows NT需要很长时间才能在Windows NT文件系统(NTFS)格式的驱动器上执行目录操作,该驱动器在单个目录中包含大量带有长文件名(名称不符合8.3约定的文件)。

当NTFS枚举目录中的文件时,它必须查找与长文件名关联的8.3名称。由于NTFS目录保持在已排序状态,因此相应的长文件名和8.3名称通常在目录列表中并不相邻。因此,NTFS对存在的每个文件使用线性搜索目录。结果,执行目录列表所需的时间随目录中文件数量的平方增加。对于少量文件(少于几百个),时间延迟可以忽略不计。但是,随着目录中文件的数量增加到几千个,执行列表所需的时间可能会增加到几分钟,几小时甚至几天。如果长文件名非常相似(仅在最后几个字符不同),则问题会更加严重。

对于以SHA1校验和命名的文件,这可能会导致灾难和糟糕的性能。

虽然以上是从Windows NT 3.5的技术说明(与NTFS 1.2 -从1995年常用于21世纪初),这也可以在的东西,如可以看出EXT3文件系统是链表的实现,需要为O(n)查找。即使有了B树更改:

尽管HTree算法大大缩短了查找时间,但可能会导致使用readdir()对大型目录中的所有文件执行某些操作的工作负载导致性能下降。
...
Daniel Phillips和Andreas Dilger提出了一个可能的缓解此性能问题的解决方案,但尚未实施,该方案涉及内核选择自由inode,这些inode的inode编号符合按文件名哈希对inode进行分组的属性。Daniel和Andreas建议根据目录的大小从一定范围的索引节点中分配索引节点,然后根据文件名哈希从该范围中选择一个空闲索引节点。从理论上讲,这应该减少当以readdir顺序访问目录中引用的inode时导致的抖动。但是,尚不清楚该策略是否会加速。实际上,这可能会增加可能必须引用的inode块的总数,从而使readdir()+ stat()工作负载的性能变差。显然,

顺便说一句,这是关于如何提高性能的,是从git发布的2005年开始的。

从Firefox和许多其他具有大量哈希缓存文件的应用程序中可以看出,按字节划分缓存的设计。它的性能成本可以忽略不计,并且当跨平台使用可能有点陈旧的系统时,很可能是程序正常与否的区别。


1
您确实注意到引用的NTFS性能文章适用于1994年发布的NT 3.5,对吗?
Avner Shahar-Kashtan's 2015年

1
@ AvnerShahar-Kashtan是的。Git于2005年发布。我知道直到2000年代初期(尽管在一家技术公司)我在公司环境中使用基于NTFS v1.2的文件系统。git的要求与当时常用系统上的文件系统之间肯定存在重叠。

如果您说这可能是引入git时技术状态的历史产物,可能会更清楚,因为就目前而言,对于2015年提出的一个问题,引用了二十多年的技术局限性(停止回答) )似乎令人困惑。
Avner Shahar-Kashtan's 2015年

公平地讲,git“打包”系统可以缓解许多此类问题。从理论上讲,git可以只使用一个目录,而当该目录中的文件数超过某个特定限制(可能与FS相关)时,可以重新打包。
nneonneo

5
@ AvnerShahar-Kashtan,如果您阅读链接的SO文章,您会发现处理包含大量文件的目录在多个文件系统和操作系统(不仅仅是NT 3.5)上是有问题的。除了文件限制之外,即使仅列出文件也可能产生大量开销。

8

这样做是有两个原因的。

目录不能任意大。例如,某些(相当现代!)文件系统在单个目录中限制为32000个条目。Linux内核中的提交数量处于该数量级。用提交的前两个十六进制数字细分提交,将顶级大小限制为256个条目。对于典型的git repos,子目录将小得多。

目录被线性扫描。在某些文件系统(例如Ext *系列)中,目录是链接列表或条目表。要查找文件,将扫描整个列表,直到找到匹配的文件名。显然,这对于性能而言是不可取的。许多现代文件系统还使用哈希表或B树进行快速查找,但并非每个人都可以使用它们。将每个目录保持较小意味着快速的访问时间。


1
“某些(相当现代!)文件系统在一个目录中限制为32000个条目。” 如果这是Git遇到的最严格的限制,那么Git使用哈希的前三个字符而不是前两个字符会更好吗?这将意味着该objects目录最多可以容纳4096个子目录,而不是被限制为256个,从而满足上述要求,但是还具有其他优点,即这些子目录最终包含自身的32000个文件的可能性要小16倍。
sampablokuper,2016年

1

这256个存储区允许git在文件系统上存储较大的存储库,从而限制目录中文件的数量,并在文件系统中性能下降,而文件系统随着包含许多文件的目录而变得越来越慢。


1

在某些文件系统和/或文件系统实现和/或libc实现中,由于大量目录条目而导致性能下降。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.