Gitは内部でオブジェクト(Blob、ツリー)を.git/objects/
フォルダーに格納します。各オブジェクトは、オブジェクトのコンテンツから計算されるSHA1ハッシュによって参照できます。
ただし、オブジェクトは.git/objects/
フォルダー内に直接格納されません。代わりに、各オブジェクトは、SHA1ハッシュのプレフィックスで始まるフォルダー内に格納されます。したがって、ハッシュb7e23ec29af22b0b4e41da31e868d57226121c84
のオブジェクトは.git/objects/b7/e23ec29af22b0b4e41da31e868d57226121c84
に格納されます
なぜGitはオブジェクトストレージをこのように細分するのですか?
Git-scmの Gitの内部のページ など、私が見つけたリソースは、howのみ説明され、なぜ。
すべてのファイルを1つのディレクトリに配置することは可能ですが、少し大きくなる場合もあります。 多くのファイルシステムには制限があります 。 USBスティック上のFAT32フォーマットのドライブにgitリポジトリを配置したいですか? 1つのディレクトリに保存できるのは65,535ファイルのみです。これは、単一のディレクトリを埋める可能性が低くなるように、ディレクトリ構造を細分化する必要があることを意味します。
これは、他のファイルシステムや大規模なgitリポジトリでも問題になります。比較的小さなgitリポジトリ(約360MiB)で、11kファイル用に181,546個のオブジェクトがあります。 Linux repo をプルすると、4,374,054個のオブジェクトが得られます。これらすべてを1つのディレクトリに配置すると、チェックアウトが不可能になり、ファイルシステムがクラッシュします(「クラッシュ」の意味で)。
そう?バイトで分割します。同様のアプローチは、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は、単一のディレクトリに長いファイル名(8.3の規則に準拠していない名前)のファイルを多数含むWindows NTファイルシステム(NTFS)でフォーマットされたドライブでディレクトリ操作を実行するのに時間がかかります。
NTFSがディレクトリ内のファイルを列挙するとき、長いファイル名に関連付けられている8.3名を検索する必要があります。 NTFSディレクトリは並べ替えられた状態で維持されるため、通常、対応する長いファイル名と8.3形式の名前は、ディレクトリリストで互いに隣接していません。したがって、NTFSは存在するすべてのファイルに対してディレクトリの線形検索を使用します。その結果、ディレクトリ一覧の実行に必要な時間は、ディレクトリ内のファイル数の2乗で増加します。少数のファイル(数百未満)の場合、時間遅延は無視できます。ただし、ディレクトリ内のファイルの数が数千に増えると、リストの実行に必要な時間が数分、数時間、または数日にも増えることがあります。長いファイル名が非常に類似している場合、問題は悪化します。最後の数文字のみが異なります。
SHA1チェックサムにちなんで名前が付けられたファイルを使用すると、これは災害およびひどいパフォーマンスのレシピになる可能性があります。
上記はWindows NT 3.5(およびNTFS 1.2-1995年から2000年代初頭に一般的に使用される)のテクニカルノートからのものですが、これは EXT with implementation ofリンクされているファイルシステム O(n)ルックアップが必要です。そして、そのBツリーの変更があっても:
HTreeアルゴリズムはルックアップ時間を大幅に改善しましたが、readdir()を使用して大きなディレクトリ内のすべてのファイルの操作を実行するワークロードのパフォーマンスが低下する可能性があります。
...
Daniel PhillipsとAndreas Dilgerによって提案されたが、まだ実装されていない、このパフォーマンスの問題を軽減するための1つの潜在的なソリューションは、iノード番号がファイル名ハッシュによってiノードをグループ化するプロパティと一致する空きiノードをカーネルが選択することを含みます。 DanielとAndreasは、ディレクトリのサイズに基づいてiノードの範囲からiノードを割り当て、ファイル名のハッシュに基づいてその範囲から空きiノードを選択することを提案しています。これにより、理論的には、ディレクトリで参照されているiノードにreaddir順にアクセスしたときに発生するスラッシングの量が減少します。ただし、この戦略によって速度が向上するかどうかは明らかではありません。実際、参照する必要があるiノードブロックの総数が増える可能性があるため、readdir()+ stat()ワークロードのパフォーマンスが低下する可能性があります。明らかに、いくつかの実験とさらなる分析がまだ必要です。
ちなみに、パフォーマンスを改善する方法についてのこのビットは2005年からで、同じ年にgitがリリースされました。
Firefoxや、多くのハッシュキャッシュファイルを持つ他の多くのアプリケーションで見られるように、バイト単位でキャッシュを分割する設計。パフォーマンスコストはごくわずかです。クロスプラットフォームを古いプラットフォームのシステムで使用すると、プログラムが動作するかどうかの違いが生じる可能性があります。
これが望ましい理由は2つあります。
ディレクトリを任意に大きくすることはできません。例一部の(合理的に最新の!)ファイルシステムは、1つのディレクトリで32000エントリに制限されています。 Linuxカーネルでのコミット数は、その桁の大きさです。コミットを最初の2桁の16進数で細分化すると、トップレベルのサイズが256エントリに制限されます。一般的なgitリポジトリでは、サブディレクトリははるかに小さくなります。
ディレクトリは直線的にスキャンされます。一部のファイルシステム(Ext *ファミリなど)では、ディレクトリはリンクされたリストまたはエントリのテーブルです。ファイルを検索するには、一致するファイル名が見つかるまでリスト全体をスキャンします。明らかに、これはパフォーマンスにとって望ましくありません。最近の多くのファイルシステムでは、ハッシュテーブルまたはBツリーを使用して高速ルックアップを行っていますが、誰もがそれらを持っているとは限りません。各ディレクトリを小さくしておくと、アクセス時間が短縮されます。
これらの256バケットを使用すると、ディレクトリ内のファイル数を制限する大きなリポジトリをgitがファイルシステムに保存し、多くのファイルを含むディレクトリで速度が低下するファイルシステムで下降パフォーマンスを提供できます。
多数のディレクトリエントリがあるとパフォーマンスが低下するファイルシステムやファイルシステムの実装、あるいはlibcの実装があります。