Perché la cartella Git .git / objects / è suddivisa in molte cartelle con prefisso SHA?


21

Git memorizza internamente oggetti (BLOB, alberi) nella .git/objects/cartella. Ogni oggetto può essere referenziato da un hash SHA1 che viene calcolato dal contenuto dell'oggetto.

Tuttavia, gli oggetti non vengono memorizzati .git/objects/direttamente nella cartella. Al contrario, ogni oggetto viene archiviato in una cartella che inizia con il prefisso dell'hash SHA1. Quindi un oggetto con l'hash b7e23ec29af22b0b4e41da31e868d57226121c84verrebbe archiviato in.git/objects/b7/e23ec29af22b0b4e41da31e868d57226121c84

Perché Git suddivide il suo archivio oggetti in questo modo?

Le risorse che ho trovato, come la pagina degli interni di Git su git-scm, mi hanno solo spiegato come , non perché .

Risposte:


33

È possibile mettere tutti i file in una directory, anche se a volte può diventare un po 'grande. Molti file system hanno un limite . Vuoi mettere un repository git su un disco formattato FAT32 su una chiavetta USB? È possibile memorizzare solo 65.535 file in una singola directory. Ciò significa che è necessario suddividere la struttura della directory in modo che sia meno probabile riempire una singola directory.

Questo potrebbe persino diventare un problema con altri file system e repository git più grandi. Un repository git relativamente piccolo che ho in giro (circa 360 MiB) e ha 181.546 oggetti per file 11k. Estrai il repository Linux e avrai 4.374.054 oggetti. Se dovessi metterli tutti in una directory, sarebbe impossibile verificare e si bloccherebbe (per un certo significato di "crash") il file system.

Così? Lo dividi per byte. Approcci simili vengono fatti con applicazioni come 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_

Oltre a ciò, si passa anche a una questione di prestazioni. Prendi in considerazione le prestazioni NTFS con numerosi nomi di file lunghi :

Windows NT impiega molto tempo per eseguire operazioni di directory su unità formattate con file system (NTFS) di Windows NT che contengono un numero elevato di file con nomi di file lunghi (nomi non conformi alla convenzione 8.3) in una singola directory.

Quando NTFS enumera i file in una directory, deve cercare i nomi 8.3 associati ai nomi di file lunghi. Poiché una directory NTFS viene mantenuta in uno stato ordinato, i nomi di file lunghi corrispondenti e i nomi 8.3 non sono generalmente uno accanto all'altro nell'elenco di directory. Pertanto, NTFS utilizza una ricerca lineare della directory per ogni file presente. Di conseguenza, il tempo necessario per eseguire un elenco di directory aumenta con il quadrato del numero di file nella directory. Per un numero limitato di file (meno di alcune centinaia) il ritardo è trascurabile. Ma poiché il numero di file in una directory aumenta a diverse migliaia, il tempo necessario per eseguire un elenco può aumentare a minuti, ore o persino giorni. Il problema si aggrava se i nomi di file lunghi sono molto simili, differendo solo negli ultimi caratteri.

Con i file che prendono il nome dai checksum SHA1, questa potrebbe essere una ricetta per disastri e prestazioni spaventose.

Mentre quanto sopra deriva da una nota tecnica di Windows NT 3.5 (e NTFS 1.2 - comunemente usati dal 1995 ai primi anni 2000), questo può essere visto anche in cose come EXT3 con implementazioni del filesystem che sono liste collegate che richiedono una ricerca O (n) . E anche con quel cambio B-tree:

Mentre l'algoritmo HTree ha migliorato significativamente i tempi di ricerca, potrebbe causare alcune regressioni delle prestazioni per i carichi di lavoro che utilizzavano readdir () per eseguire alcune operazioni di tutti i file in una directory di grandi dimensioni.
...
Una potenziale soluzione per mitigare questo problema di prestazioni, che è stata suggerita da Daniel Phillips e Andreas Dilger, ma non ancora implementata, prevede che il kernel scelga inode liberi i cui numeri di inode soddisfino una proprietà che raggruppa gli inode in base al loro hash di nome file. Daniel e Andreas suggeriscono di allocare l'inode da un intervallo di inode in base alla dimensione della directory, e quindi di scegliere un inode gratuito da quell'intervallo in base all'hash del nome file. Ciò dovrebbe in teoria ridurre la quantità di thrashing che risulta quando si accede agli inode a cui si fa riferimento nella directory in ordine readdir. Non è chiaro, tuttavia, che questa strategia si tradurrà in un aumento di velocità; in effetti potrebbe aumentare il numero totale di blocchi di inode a cui potrebbe essere necessario fare riferimento, e quindi peggiorare le prestazioni dei carichi di lavoro readdir () + stat (). Chiaramente,

Per inciso, questo pezzo su come migliorare le prestazioni è del 2005, lo stesso anno in cui è stato rilasciato git.

Come si è visto con Firefox e molte altre applicazioni che hanno molti file con cache hash, la progettazione di suddividere la cache per byte. Ha costi di prestazione trascurabili e, se usati su piattaforme diverse con sistemi che potrebbero essere un po 'vecchi, potrebbe benissimo essere la differenza tra il funzionamento del programma o meno.


1
Hai notato che l'articolo sulle prestazioni di NTFS che citi si applica a NT 3.5, pubblicato nel 1994, giusto?
Avner Shahar-Kashtan,

1
@ Sì AvnerShahar-Kashtan. Git è stato rilasciato nel 2005. So cosa stavo usando i file system basati su NTFS v1.2 in un ambiente aziendale già nei primi anni 2000 (in un'azienda tecnologica). Vi è sicuramente una sovrapposizione tra i requisiti di git e i file system sui sistemi comunemente disponibili al momento.

Forse sarebbe più chiaro se affermassi che questo potrebbe essere un artefatto storico dello stato della tecnologia quando Git è stato introdotto, perché così com'è, per una domanda posta nel 2015, citando una limitazione tecnica di vent'anni (riprendendo la risposta ) sembra confuso.
Avner Shahar-Kashtan,

Ad essere onesti, gitil sistema "pack" mitiga molti di questi problemi. Teoricamente, gitpotrebbe usare solo una singola directory, e semplicemente reimballare quando il numero di file in quella directory ha superato un certo limite (possibilmente dipendente da FS).
nneonneo,

5
@ AvnerShahar-Kashtan se leggi l'articolo SO collegato puoi vedere che gestire le directory che contengono un gran numero di file è problematico su più file system e sistemi operativi, non solo su NT 3.5. Limiti dei file a parte, anche solo elencare i file può comportare una grande quantità di sovraccarico.

8

Ci sono due ragioni per cui questo è desiderabile.

Le directory non possono essere arbitrariamente grandi. Ad esempio, alcuni filesystem (ragionevolmente moderni!) Sono limitati a 32000 voci in una singola directory. Il numero di commit nel kernel Linux è in questo ordine di grandezza. La suddivisione dei commit per le prime due cifre esadecimali limita la dimensione di livello superiore a 256 voci. Le sottodirectory saranno molto più piccole per i tipici repository git.

Le directory vengono scansionate in modo lineare. In alcuni file system (ad es. La famiglia Ext *), una directory è un elenco o una tabella di voci collegate. Per cercare un file, l'intero elenco viene scansionato fino a quando non viene trovato un nome file corrispondente. Chiaramente, questo non è auspicabile per le prestazioni. Molti file system moderni usano inoltre tabelle hash o alberi B per una ricerca veloce, ma non tutti possono averli. Mantenere ogni directory piccola significa tempi di accesso rapidi.


1
"alcuni filesystem (ragionevolmente moderni!) sono limitati a 32000 voci in una singola directory." Se questa è la limitazione più severa che Git sta incontrando, non sarebbe meglio per Git aver usato i primi tre caratteri dell'hash, invece dei primi due ? Ciò significherebbe che la objectsdirectory potrebbe contenere fino a 4096 sottodirectory invece di essere limitata a 256, soddisfacendo i requisiti di cui sopra, ma con l'ulteriore vantaggio che tali sottodirectory avrebbero una probabilità 16 volte inferiore di finire per contenere> 32000 file stessi.
sampablokuper,

1

Questi 256 bucket consentono a git di archiviare repository più grandi su file system che limitano i file numerici in una directory e forniscono prestazioni di discesa su file system che diventano più lenti con le directory che contengono molti file.


1

Esistono alcuni file system e / o implementazioni di file system e / o implementazioni libc in cui le prestazioni diminuiscono con un numero elevato di voci di directory.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.