Come risolvere il limite numerico delle sottodirectory linux?


9

Ho un sito Web che memorizzerà le immagini del profilo utente. Ogni immagine è memorizzata in una directory (Linux) specifica per l'utente. Attualmente ho una base di clienti di oltre 30, il che significa che avrò più di 30 cartelle. Ma il mio attuale box Linux (ext2 / ext3) non supporta la creazione di oltre 32000 directory. Come posso superare questo? Anche i ragazzi di YouTube hanno lo stesso problema, con le miniature dei video. Ma l'hanno risolto passando a ReiserFS. Non possiamo avere una soluzione migliore?

Aggiornamento: quando è stato chiesto in IRC, la gente chiedeva di aggiornarlo a ext4, che ha un limite di 64k e ovviamente puoi anche superarlo . O l'hacking del kernel per modificare il limite.

Aggiornamento: che ne dite di dividere la base utenti in cartelle in base all'intervallo userid. Significa 1-1000 in una cartella, 1000-2000 nell'altra in questo modo. Questo sembra essere semplice. Che ne dici ragazzi?

Francamente, non c'è altro modo?


1
Perché non vuoi cambiare il filesystem? Se questa è una limitazione di ext2 / 3 non avrai altre modifiche se non cambiare il filesystem o dividere l'attuale FS in FS più piccoli (punti di mount più diversi).
Manuel Faux,

1
Manuel: Se cambia il file system, lega un FS specifico alla sua applicazione. Anche se questa potrebbe essere la risposta, vorrei che questo sia probabilmente un problema che deve essere risolto a livello di applicazione. Se hai bisogno di hackerare il kernel o il file system, probabilmente stai seguendo una strada sbagliata a meno che non ci siano dei requisiti molto speciali.
Kyle Brandt,

Risposte:


16

Tale limite è per directory, non per l'intero filesystem, quindi è possibile aggirare il problema suddividendo ulteriormente le cose. Ad esempio, invece di avere tutte le sottodirectory dell'utente nella stessa directory, dividerle per i primi due caratteri del nome in modo da avere qualcosa del tipo:

top_level_dir
|---aa
|   |---aardvark1
|   |---aardvark2
|---da
|   |---dan
|   |---david
|---do
    |---don

Ancora meglio sarebbe creare una qualche forma di hash dei nomi e usarla per la divisione. In questo modo otterrai una migliore diffusione tra le directory anziché, con l'esempio delle lettere iniziali, "da" è molto pieno e "zz" completamente vuoto. Ad esempio se prendi il nome CRC o MD5 e usi i primi 8 bit otterrai qualcosa del tipo:

top_level_dir
|---00
|   |---some_username
|   |---some_username
|---01
|   |---some_username
...
|---FF
|   |---some_username

Questo può essere esteso a ulteriori profondità, se necessario, ad esempio in questo modo se si utilizza il nome utente non un valore hash:

top_level_dir
|---a
|   |---a
|       |---aardvark1
|       |---aardvark2
|---d
    |---a
    |   |---dan
    |   |---david
    |---o
        |---don

Questo metodo viene utilizzato in molti luoghi come la cache di calamari, per copiare l'esempio di Ludwig e le cache locali dei browser web.

Una cosa importante da notare è che con ext2 / 3 inizierai a colpire i problemi di prestazioni prima di avvicinarti comunque al limite di 32.000, poiché le directory vengono cercate in modo lineare. Passare a un altro filesystem (ext4 o reiser per esempio) rimuoverà questa inefficienza (reiser cerca le directory con un algoritmo suddiviso in binari in modo che le directory lunghe siano gestite in modo molto più efficiente, ext4 potrebbe fare altrettanto) e il limite fisso per directory.


Ho appena aggiornato la descrizione della domanda per includere questo: "Aggiornamento: che ne dite di dividere la base utenti in cartelle in base all'intervallo userid. Significa 1-1000 in una cartella, 1000-2000 nell'altra in questo modo. Questo sembra essere semplice. Cosa dici?"
Nessuno-da

1
Funzionerebbe bene e sarebbe più efficiente di un hash, se gli utenti sono generalmente identificati dall'ID utente anziché (o anche dal nome utente). Tuttavia, se ti riferisci sempre a loro per nome altrove nel sistema, dovrai aggiungere ulteriori ricerche nome-> id in tutto il luogo.
David Spillett,

Grazie David! Ho provato anche una soluzione diversa. Ho creato quasi 4 cartelle con l'intervallo 1-30000, 30000-60000 ecc. Penso che ottenere un file da una directory così grande richiederà più tempo che da una directory che ha 1000 file (approccio precedente). Che ne dici?
Nessuno-da

1
Dipende dal filesystem. Se stai usando ext2 o ext3, consiglierei molto più piccolo di 30.000 per directory. Alcuni strumenti emettono avvisi di circa 10.000. È possibile attivare l'indicizzazione della directory in ext3 / 4 per aiutare: tune2fs -O dir_index / dev / <nome_volume> ma mantenere il numero di oggetti in una directory più basso (un paio di migliaia o meno?) È ciò che consiglierei qui .
David Spillett,

@Maddy, vuoi questa soluzione a causa di altre limitazioni su come Ext2 / 3 gestisce un gran numero di file. Vedere serverfault.com/questions/43133/… per alcuni dettagli. Suddividere i nomi in secchi come sottodirectory allevia altri problemi che potresti incontrare alla fine. Si noti che questa è la stessa strategia utilizzata da Squid quando imposta la cache degli oggetti per la prima volta, ad esempio 64 directory ognuna con 64 directory al suo interno, solo come esempio.
Avery Payne,

7

Se sei legato a ext2 / ext3 l'unica possibilità che vedo è quella di partizionare i tuoi dati. Trova un criterio che divide i tuoi dati in blocchi gestibili di dimensioni simili.

Se riguarda solo le immagini del profilo, farei:

  1. Usa un hash (es. SHA1) dell'immagine
  2. Utilizzare SHA1 come nome di file e directory

Ad esempio la cache SQUID lo fa in questo modo:

f / 4b / 353ac7303854033

La directory di livello superiore è la prima cifra esadecimale, il secondo livello è le successive due cifre esadecimali e il nome del file è le cifre esadecimali rimanenti.


2

Non possiamo avere una soluzione migliore?

Hai una soluzione migliore: usa un filesystem diverso, ce ne sono molti disponibili, molti dei quali sono ottimizzati per compiti diversi. Come hai sottolineato, ReiserFS è ottimizzato per la gestione di molti file in una directory.

Vedi qui per un confronto dei filesystem.

Sii contento di non essere bloccato con NTFS, che è davvero spaventoso per molti file in una directory. Consiglierei JFS come sostituto se non ti va di usare il ext4 FS relativamente nuovo (ma apparentemente stabile).


Hai buoni collegamenti alle prestazioni del filesystem NTFS?
Thorbjørn Ravn Andersen,

sì, a parte l'esperienza personale con un'app che è rimasta troppo a lungo creando nuovi file in una directory .. (ci sono volute ore per eliminarli tutti) e le prestazioni di sovversione aumentano limitando il numero di file in una directory a 1000. Oppure leggi : support.microsoft.com/kb/130694 Non credo che abbiano mai "risolto" questo dato che è stato ancora notato come perf. tweak per NTFS.
gbjbaanb,

1

L'immagine del profilo è piccola? Che ne dici di metterlo nel database con il resto dei dati del profilo? Questa potrebbe non essere l'opzione migliore per te, ma vale la pena considerare ...

Ecco un white paper Microsoft (più vecchio) sull'argomento: BLOB o non BLOB .


1

Ho hackerato insieme una piccola galleria web, dove ho finito con una variazione di questo problema; Ho "solo" avuto ~ 30.000 immagini nella directory della cache, che si è rivelata piuttosto lenta (ext2 usa liste collegate per gli indici delle directory, come me lo ricordo).

Ho finito per fare qualcosa del genere:

def key2path(key):
    hash = md5(key)
    return os.path.join(hash[0], hash[1], key)

Questo partizionerà i dati in 256 directory, il che fornisce una rapida ricerca nella directory per ciascuno dei tre livelli.

  • Ho scelto di usare MD5 su SHA-1, poiché MD5 garantisce un output diverso se si cambiano 12 bit da 32, quindi trovo che si adatti perfettamente a nomi utente, directory e altre cose brevi. Ed è anche veloce ...
  • Non includo l'intero hash, poiché produrrà troppe directory e cestinerà ripetutamente la cache del disco.

1
Probabilmente potresti usare un hash più semplice come CRC, poiché l'hash non deve essere crittograficamente forte come MD5 o SHA ... ma la differenza di prestazioni è probabilmente comunque trascurabile ...
sleske,

0

Non una risposta immediata al tuo problema, ma qualcosa da guardare per riferimento futuro è il progetto collegato OpenBSD chiamato 'Epitome'

Epitome è un motore che fornisce servizi di archiviazione a istanza singola, archiviazione indirizzabile al contenuto e deduplicazione.

Tutti i tuoi dati sono archiviati in un archivio dati come blocchi con hash, rimuovendo blocchi non univoci per ridurre l'utilizzo dello spazio e ti consentono essenzialmente di dimenticare il meccanismo di archiviazione in quanto puoi semplicemente richiedere il contenuto dall'archivio dati da UUID.

Epitome è attualmente sperimentale, ma qualcosa da guardare per il futuro.


0

Generalmente si desidera evitare di avere directory con un numero elevato di file / directory. Il motivo principale è che l'espansione dei caratteri jolly sulla riga di comando comporterà errori "Troppi argomenti" con conseguente dolore durante il tentativo di lavorare con queste directory.

Cerca una soluzione che renda un albero più profondo ma più stretto, ad esempio creando sottocartelle come altri hanno descritto.


0

Abbiamo avuto un problema simile, la soluzione - come accennato in precedenza - è quella di creare una gerarchia di directory.

Ovviamente se hai un'applicazione complessa che si basa su una struttura di directory piatta, probabilmente avrai bisogno di molte patch. Quindi è bene sapere che esiste una soluzione alternativa, utilizzare i collegamenti simbolici che non hanno il limite di 32k menzionato. Quindi hai un sacco di tempo per riparare l'app ...


0

Perché non utilizzare un approccio timestamp e quindi disporre di un'opzione di overflow.

Per esempio

Quindi supponiamo che il tuo timestamp sia: 1366587600

Ometti le ultime 2 cifre (oppure diventa leggermente ridicolo). Separare il timbro in gruppi di 4 (il conteggio delle directory non dovrebbe raggiungere più di 9999 - se lo si desidera è possibile separarlo in modo diverso).

Questo dovrebbe lasciarti con qualcosa del genere:

/files/1366/5876/

Quindi controlla anche l'importo all'interno della directory prima del caricamento, se sta ottenendo un gran numero di caricamenti (ovvero 32000 + per 100 secondi), quindi esegui l'iterazione della directory con la seconda o una lettera, ad esempio:

/files/1366/5876/a/file.txt

o

/files/1366/5876/00/file.txt

Quindi registra il timestamp + la lettera o il codice del percorso completo in un db insieme all'utente e dovresti essere impostato.

percorso: 1366587600 o 13665876a (se si usano le lettere).

Questo finisce con un gran numero di directory, ma può essere davvero utile per gestire le revisioni dei file. Ad esempio, se un utente desidera utilizzare una nuova immagine del profilo, hai ancora la vecchia versione con data e ora di quella precedente nel caso in cui desiderasse annullare le modifiche (non solo sovrascritte).


0

Suggerirei di decidere quante sottodirectory massime vuoi (o puoi) avere nella cartella principale.

Quindi devi convertire il tuo ID utente in modo che inizino da 1.

Quindi puoi fare: modulo = currentId % numberOfSubdirectories

moduloconterrà ora il numero della tua sottodirectory che non sarà mai maggiore di quello numberOfSubdirectoriesche hai scelto.

Fai quello che vuoi con modulo, l'hash, per esempio.

Anche in questo modo le sottodirectory verranno riempite in modo lineare.

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.