Milioni di (piccoli) file di testo in una cartella


15

Vorremmo archiviare milioni di file di testo in un filesystem Linux, con lo scopo di essere in grado di comprimere e servire una raccolta arbitraria come servizio. Abbiamo provato altre soluzioni, come un database chiave / valore, ma i nostri requisiti di concorrenza e parallelismo rendono l'utilizzo del filesystem nativo la scelta migliore.

Il modo più semplice è archiviare tutti i file in una cartella:

$ ls text_files/
1.txt
2.txt
3.txt

che dovrebbe essere possibile su un file system EXT4 , che non ha limiti al numero di file in una cartella.

I due processi FS saranno:

  1. Scrivi file di testo dal web scrape (non dovrebbe essere influenzato dal numero di file nella cartella).
  2. Comprimi i file selezionati, forniti da un elenco di nomi di file.

La mia domanda è: l'archiviazione di un massimo di dieci milioni di file in una cartella influirà sulle prestazioni delle operazioni di cui sopra o sulle prestazioni generali del sistema, diversamente dalla creazione di un albero di sottocartelle in cui i file devono vivere?


4
Correlati: Come correggere gli errori intermittenti "Nessuno spazio lasciato sul dispositivo" durante MV quando il dispositivo ha molto spazio . L'uso dir_index, che è spesso abilitato per impostazione predefinita, velocizzerà le ricerche ma potrebbe limitare il numero di file per directory.
Mark Plotnick,

Perché non provarlo rapidamente su una macchina virtuale e vedere com'è? Con bash è banale popolare una cartella con un milione di file di testo con caratteri casuali all'interno. Sento che otterrai informazioni davvero utili in questo modo, oltre a quello che imparerai qui.
Giosuè,

2
@JoshuaD: Se lo popoli tutto in una volta su un nuovo FS, probabilmente avrai tutti gli inode contigui sul disco, quindi ls -lo qualsiasi altra cosa che statsia ogni inode nella directory (es. bashGlobbing / completamento della scheda) sarà artificialmente più veloce che dopo un po 'di usura (cancellare alcuni file, scriverne di nuovi). ext4 potrebbe fare di meglio con XFS, perché XFS alloca dinamicamente lo spazio per gli inode rispetto ai dati, in modo da poter finire con inode più dispersi, credo. (Ma questa è una supposizione pura basata su una conoscenza molto poco dettagliata; ho appena usato ext4). Vai con abc/def/subdir.
Peter Cordes,

Sì, non credo che il test suggerito sarà in grado di dire all'OP "questo funzionerà", ma potrebbe sicuramente dirgli rapidamente "questo non funzionerà", il che è utile.
Giosuè il

1
ma i nostri requisiti di concorrenza e parallelismo rendono l'utilizzo del filesystem nativo la scelta migliore Cosa hai provato? D'altro canto, penso che anche un RDBMS di fascia bassa come MySQL e un servlet Java con cui creare i file zip al voloZipOutputStream batterebbero praticamente qualsiasi filesystem nativo Linux gratuito - dubito che tu voglia pagare per il GPFS di IBM. Il ciclo per elaborare un set di risultati JDBC e rendere tale flusso zip è probabilmente solo 6-8 righe di codice Java.
Andrew Henle,

Risposte:


10

Il lscomando, o anche il completamento TAB o l'espansione jolly da parte della shell, presenteranno normalmente i loro risultati in ordine alfanumerico. Ciò richiede la lettura dell'intero elenco di directory e l'ordinamento. Con dieci milioni di file in una singola directory, questa operazione di ordinamento richiederà un tempo non trascurabile.

Se riesci a resistere all'impulso del completamento di TAB e ad esempio scrivi i nomi dei file da comprimere per intero, non dovrebbero esserci problemi.

Un altro problema con i caratteri jolly potrebbe essere l'espansione dei caratteri jolly che probabilmente produce più nomi di file di quelli che si adattano a una riga di comando di lunghezza massima. La lunghezza massima tipica della riga di comando sarà più che adeguata per la maggior parte delle situazioni, ma quando parliamo di milioni di file in una singola directory, questo non è più un presupposto sicuro. Quando viene superata una lunghezza massima della riga di comando nell'espansione dei caratteri jolly, la maggior parte delle shell fallisce semplicemente l'intera riga di comando senza eseguirla.

Questo può essere risolto eseguendo le operazioni con i caratteri jolly utilizzando il findcomando:

find <directory> -name '<wildcard expression>' -exec <command> {} \+

o una sintassi simile quando possibile. Il find ... -exec ... \+prenderà automaticamente in considerazione la lunghezza della linea di comando massima, ed eseguirà il comando come numero di volte necessario durante il montaggio la quantità massima di nomi di file per ogni linea di comando.


I filesystem moderni usano B, B + o alberi simili per mantenere le voci della directory. en.wikipedia.org/wiki/HTree
dimm

4
Sì ... ma se la shell o il lscomando non vengono a sapere che l'elenco di directory è già ordinato, impiegheranno comunque il tempo per eseguire l'algoritmo di ordinamento. Inoltre, lo spazio utente potrebbe utilizzare un ordinamento localizzato (LC_COLLATE) che potrebbe essere diverso da quello che il filesystem potrebbe fare internamente.
telcoM

17

Questo è pericolosamente vicino a una domanda / risposta basata sull'opinione ma cercherò di fornire alcuni fatti con le mie opinioni.

  1. Se si dispone di un numero molto elevato di file in una cartella, qualsiasi operazione basata su shell che tenti di enumerarli (ad es. mv * /somewhere/else) Potrebbe non riuscire a espandere correttamente il carattere jolly oppure il risultato potrebbe essere troppo grande da utilizzare.
  2. ls richiederà più tempo per enumerare un numero molto elevato di file rispetto a un numero ridotto di file.
  3. Il filesystem sarà in grado di gestire milioni di file in una singola directory, ma le persone probabilmente avranno difficoltà.

Una raccomandazione è quella di dividere il nome del file in blocchi di due, tre o quattro caratteri e usarli come sottodirectory. Ad esempio, somefilename.txtpotrebbe essere memorizzato come som/efi/somefilename.txt. Se stai usando nomi numerici, dividi da destra a sinistra anziché da sinistra a destra in modo che ci sia una distribuzione più uniforme. Ad esempio, 12345.txtpotrebbe essere memorizzato come 345/12/12345.txt.

È possibile utilizzare l'equivalente di zip -j zipfile.zip path1/file1 path2/file2 ...per evitare di includere i percorsi della sottodirectory intermedia nel file ZIP.

Se stai servendo questi file da un server web (non sono del tutto sicuro che sia pertinente) è banale nascondere questa struttura a favore di una directory virtuale con regole di riscrittura in Apache2. Suppongo che lo stesso sia vero per Nginx.


L' *espansione avrà esito positivo a meno che non si esaurisca la memoria, ma a meno che non si aumenti il ​​limite di stack (su Linux) o si usi una shell in cui mvè incorporato o può essere incorporato (ksh93, zsh), la execve()chiamata di sistema potrebbe non riuscire con un errore E2BIG.
Stéphane Chazelas,

@ StéphaneChazelas sì ok, la mia scelta di parole avrebbe potuto essere migliore, ma l'effetto netto per l'utente è più o meno lo stesso. Vedrò se posso modificare leggermente le parole senza impantanarmi nella complessità.
roaima,

Sei curioso di sapere come comprimeresti quel file zip se evitassi di includere i percorsi della sottodirectory intermedia in esso, senza incorrere nei problemi che stai discutendo?
Polpo,

1
@Octopus l'OP afferma che il file zip conterrà " file selezionati, dati dall'elenco dei nomi di file ".
roaima,

Consiglio di utilizzare zip -j - ...e reindirizzare il flusso di output direttamente alla connessione di rete del client zip -j zipfile.zip .... Scrivere un file zip reale su disco significa che il percorso dei dati viene letto dal disco-> comprim-> scrivi sul disco-> leggi dal disco-> invia al client. Ciò può triplicare i requisiti di I / O del disco rispetto alla lettura da disco-> comprim-> invia al client.
Andrew Henle,

5

Gestisco un sito Web che gestisce un database per film, TV e videogiochi. Per ognuna di queste sono presenti più immagini con TV contenenti dozzine di immagini per spettacolo (ad esempio istantanee di episodi ecc.).

Ci sono molti file di immagini. Da qualche parte nella gamma di oltre 250.000. Questi sono tutti memorizzati in un dispositivo di archiviazione a blocchi montato in cui il tempo di accesso è ragionevole.

Il mio primo tentativo di memorizzare le immagini è stato in una singola cartella come /mnt/images/UUID.jpg

Ho incontrato le seguenti sfide.

  • lstramite un terminale remoto si bloccherebbe e basta. Il processo sarebbe andato zombie eCTRL+C non lo spezzerebbe.
  • prima di raggiungere quel punto qualsiasi lscomando riempirebbe rapidamente il buffer di output eCTRL+C non fermerebbe lo scrolling infinito.
  • La compressione di 250.000 file da una singola cartella ha richiesto circa 2 ore. È necessario eseguire il comando zip staccato dal terminale, altrimenti qualsiasi interruzione della connessione significa che è necessario ricominciare da capo.
  • Non rischierei di provare a utilizzare il file zip su Windows.
  • La cartella divenne rapidamente una zona vietata agli umani .

Ho finito per dover archiviare i file in sottocartelle usando il tempo di creazione per creare il percorso. Come /mnt/images/YYYY/MM/DD/UUID.jpg. Ciò ha risolto tutti i problemi di cui sopra e mi ha permesso di creare file zip destinati a una data.

Se l'unico identificativo per un file che hai è un numero numerico e questi numeri tendono ad essere eseguiti in sequenza. Perché non raggrupparli per 100000, 10000e1000 .

Ad esempio, se si dispone di un file denominato, 384295.txtil percorso sarebbe:

/mnt/file/300000/80000/4000/295.txt

Se sai che raggiungerai qualche milione. Usa 0prefissi per 1.000.000

/mnt/file/000000/300000/80000/4000/295.txt

1

Scrivi file di testo dal web scrape (non dovrebbe essere influenzato dal numero di file nella cartella).

Per creare un nuovo file è necessario eseguire la scansione del file di directory alla ricerca di spazio sufficiente per la nuova voce di directory. Se non viene individuato spazio sufficiente per memorizzare la nuova voce della directory, verrà posizionata alla fine del file della directory. All'aumentare del numero di file in una directory, aumenta anche il tempo di scansione della directory.

Fintanto che i file di directory rimangono nella cache di sistema, l'hit prestazioni da questo non sarà negativo, ma se i dati vengono rilasciati, la lettura del file di directory (di solito altamente frammentato) dal disco potrebbe richiedere un bel po 'di tempo. Un SSD migliora questo, ma per una directory con milioni di file, potrebbe esserci ancora un notevole impatto sulle prestazioni.

Comprimi i file selezionati, forniti da un elenco di nomi di file.

È probabile che ciò richieda tempo aggiuntivo in una directory con milioni di file. In un file system con voci di directory con hash (come EXT4), questa differenza è minima.

la memorizzazione di un massimo di dieci milioni di file in una cartella influirà sulle prestazioni delle operazioni di cui sopra o sulle prestazioni generali del sistema, diversamente dalla creazione di un albero di sottocartelle in cui i file devono vivere?

Un albero di sottocartelle non presenta nessuno degli svantaggi delle prestazioni sopra indicati. Inoltre, se il file system sottostante viene modificato per non avere nomi di file con hash, la metodologia ad albero funzionerà comunque bene.


1

In primo luogo: impedire a 'ls' di ordinare con 'ls -U', magari aggiornare ~ / bashrc per avere 'alias ls = "ls -U"' o simile.

Per il tuo set di file di grandi dimensioni, puoi provare questo in questo modo:

  • creare un set di file di test

  • vedere se molti nomi di file causano problemi

  • utilizzare xargs parmeter-batching e il comportamento (predefinito) di zip nell'aggiungere file a uno zip per evitare problemi.

Questo ha funzionato bene:

# create ~ 100k files
seq 1 99999 | sed "s/\(.*\)/a_somewhat_long_filename_as_a_prefix_to_exercise_zip_parameter_processing_\1.txt/" | xargs touch
# see if zip can handle such a list of names
zip -q /tmp/bar.zip ./*
    bash: /usr/bin/zip: Argument list too long
# use xargs to batch sets of filenames to zip
find . -type f | xargs zip -q /tmp/foo.zip
l /tmp/foo.zip
    28692 -rw-r--r-- 1 jmullee jmullee 29377592 2017-12-16 20:12 /tmp/foo.zip
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.