Archiviazione efficace di centinaia di milioni di piccoli file. Soluzione self-hosted

Archiviazione efficace di centinaia di milioni di piccoli file. Soluzione self-hosted

Gentile comunità, questo articolo sarà dedicato alla memorizzazione e distribuzione efficace di centinaia di milioni di piccoli file. In questa fase, viene proposta una soluzione finale per i sistemi di file compatibili con POSIX, con supporto completo per il blocco, incluso quello cluster, e sembra che anche senza soluzioni temporanee.

Pertanto, per questo scopo ho scritto il mio server specializzato.
Durante l'implementazione di questo compito sono riuscito a risolvere il problema principale e nel contempo ottenere un risparmio di spazio su disco e di memoria, che la nostra infrastruttura di file cluster consumava. In effetti, una tale quantità di file è dannosa per qualsiasi sistema di file cluster.

L'idea è questa:

In parole semplici, attraverso il server vengono caricati piccoli file, che vengono salvati direttamente in un archivio e letti allo stesso modo, mentre i file grandi vengono posizionati accanto. Schema: 1 cartella = 1 archivio, il che significa che abbiamo diversi milioni di archivi con piccoli file, invece di diverse centinaia di milioni di file. E tutto ciò è realizzato in modo completo, senza alcun script o distribuzione di file in archivi tar/zip.

Cercherò di esprimermi in modo più conciso, mi scuso in anticipo se il post sarà denso.

Tutto è iniziato perché non riuscivo a trovare un server adatto nel mondo che potesse salvare i dati ricevuti tramite il protocollo HTTP direttamente negli archivi, evitando gli svantaggi tipici degli archivi comuni e degli oggetti di memorizzazione. La ragione della mia ricerca è stata un cluster Origin di 10 server cresciuto enormemente, in cui si erano accumulati già 250.000.000 piccoli file, e la tendenza alla crescita non accennava a fermarsi.

Per chi non ama leggere articoli e preferisce una documentazione più semplice:

qui e qui.

E anche con Docker, al momento c'è un'opzione solo insieme a nginx per sicurezza:

docker run -d --restart=always -e host=localhost -e root=\/var\/storage 
-v \/var\/storage:\/var\/storage --name wzd -p 80:80 eltaline\/wzd

Successivamente:

Se ci sono molti file, sono necessarie risorse significative, e la cosa più frustrante è che parte di esse va sprecata. Ad esempio, nell'utilizzare un file system a cluster (in questo caso, MooseFS) un file, indipendentemente dalla dimensione reale, occupa sempre almeno 64 KB. Ciò significa che per file delle dimensioni di 3, 10 o 30 KB su disco servono 64 KB. Se ci sono un quarto di miliardo di file, perdiamo da 2 a 10 terabyte. Non è possibile creare nuovi file all'infinito, poiché anche in MooseFS c'è un limite: non più di 1 miliardo con una replica di ciascun file.

Con l'aumentare del numero di file, è necessaria molta memoria RAM per i metadati. Inoltre, i grandi dump di metadati frequenti contribuiscono all'usura degli SSD.

Server wZD. Mettiamo ordine nei dischi.

Il server è scritto in linguaggio Go. Prima di tutto, dovevo ridurre il numero di file. Come farlo? Attraverso l'archiviazione, ma in questo caso senza compressione, poiché i miei file sono immagini compresse. A soccorrere è arrivato BoltDB, a cui ho dovuto rimuovere alcune carenze, come indicato nella documentazione.

In totale, invece di un quarto di miliardo di file, nel mio caso sono rimasti solo 10 milioni di archivi Bolt. Se avessi potuto cambiare la struttura attuale di riempimento delle directory con i file, sarebbe stato possibile ridurre a circa 1 milione di file.

Tutti i file piccoli vengono impacchettati in archivi Bolt, che ricevono automaticamente i nomi delle directory in cui si trovano, mentre tutti i file di grandi dimensioni rimangono accanto agli archivi, non ha senso impacchettarli, è configurabile. Piccoli — archiviati, grandi — lasciati invariati. Il server funziona in modo trasparente con entrambi.

Architettura e caratteristiche del server wZD.

Archiviazione efficace di centinaia di milioni di piccoli file. Soluzione self-hosted

Il server funziona sotto i sistemi operativi Linux, BSD, Solaris e OSX. Ho testato solo per l'architettura AMD64 su Linux, ma dovrebbe funzionare anche per ARM64, PPC64, MIPS64.

Caratteristiche principali:

  • Multithreading;
  • Multi-server, offrendo tolleranza ai guasti e bilanciamento del carico;
  • Massima trasparenza per l'utente o lo sviluppatore;
  • Metodi HTTP supportati: GET, HEAD, PUT e DELETE;
  • Gestione del comportamento durante la lettura e la scrittura tramite intestazioni client;
  • Supporto per host virtuali configurabili in modo flessibile;
  • Supporto per la verifica di integrità dei dati CRC durante la scrittura/lettura;
  • Buffer semi-dinamici per un consumo minimo di memoria e un'ottimizzazione delle prestazioni di rete;
  • Compattazione dei dati in attesa;
  • Inoltre, è disponibile l'archiviatore multithreading wZA per la migrazione dei file senza fermare il servizio.

Esperienza reale:

Ho sviluppato e testato un server e un archiviatore su dati in tempo reale per un periodo piuttosto lungo, e ora funziona con successo su un cluster che include 250.000.000 file di piccole dimensioni (immagini), situati in 15.000.000 directory su dischi SATA separati. Il cluster, composto da 10 server, funge da server Origin, posizionato dietro una rete CDN. Per la sua gestione, sono utilizzati 2 server Nginx + 2 server wZD.

A coloro che decidono di utilizzare questo server, ha senso pianificare la struttura delle directory prima dell'uso, se applicabile. Preciso subito che il server non è destinato a contenere tutto in un solo archivio Bolt.

Test delle prestazioni:

Più piccolo è il file archiviato, più velocemente sono eseguite le operazioni GET e PUT su di esso. Confrontiamo il tempo totale di scrittura da parte di un client HTTP su file normali e su archivi Bolt, così come la lettura. Viene confrontato il lavoro con file di dimensioni 32 KB, 256 KB, 1024 KB, 4096 KB e 32768 KB.

Durante l'uso degli archivi Bolt, viene verificata l'integrità dei dati di ogni file (viene utilizzato il CRC), e sia prima che dopo la scrittura avviene una lettura in tempo reale e un ricalcolo; questo naturalmente introduce dei ritardi, ma la cosa principale è la sicurezza dei dati.

Ho effettuato i test delle prestazioni su unità SSD, poiché sui dischi SATA i test non mostrano differenze chiare.

Grafici dei risultati dei test:

Archiviazione efficace di centinaia di milioni di piccoli file. Soluzione self-hosted
Archiviazione efficace di centinaia di milioni di piccoli file. Soluzione self-hosted

Come si può vedere, per file di piccole dimensioni la differenza nel tempo di lettura e scrittura tra file archiviati e non archiviati è ridotta.

Una situazione completamente diversa si avrà nel test di lettura e scrittura di file di dimensioni 32 MB:

Archiviazione efficace di centinaia di milioni di piccoli file. Soluzione self-hosted

La differenza di tempo tra la lettura dei file è nell'ordine dei 5-25 ms. Per quanto riguarda la scrittura, la situazione è peggiore, la differenza è di circa 150 ms. Tuttavia, in questo caso non è necessario caricare file di grandi dimensioni, non ha semplicemente senso; possono vivere separatamente dagli archivi.

*Tecnicamente, è possibile utilizzare questo server anche per compiti che richiedono NoSQL.

Principali metodi di lavoro con il server wZD:

Caricamento di un file normale:

curl -X PUT --data-binary @test.jpg http://localhost/test/test.jpg

Caricamento di un file in un archivio Bolt (se non viene superato il parametro server fmaxsize, che determina la dimensione massima del file che può essere incluso nell'archivio; se viene superato, il file verrà caricato normalmente accanto all'archivio):

curl -X PUT -H "Archive: 1" --data-binary @test.jpg http://localhost/test/test.jpg

Download del file (se sul disco e nell'archivio ci sono file con nomi identici, il file non compresso ha la priorità di default durante il download):

curl -o test.jpg http://localhost/test/test.jpg

Download del file dall'archivio Bolt (forzato):

curl -o test.jpg -H "FromArchive: 1" http://localhost/test/test.jpg

La descrizione di altri metodi è disponibile nella documentazione.

Documentazione wZD
Documentazione wZA

Il server attualmente supporta solo il protocollo HTTP, mentre HTTPS non è ancora funzionante. Anche il metodo POST non è supportato (non è ancora stato deciso se sia necessario o meno).

Chi scaverà nel codice sorgente troverà una caramella, non tutti la amano, ma non ho legato il codice principale alle funzioni del framework web, tranne per il gestore delle eccezioni, quindi in futuro posso riscriverlo rapidamente su quasi qualsiasi motore.

ToDo:

  • Sviluppo di un proprio replicatore e distributore + geo per la possibilità di utilizzo in grandi sistemi senza sistemi di file cluster (Tutto da adulti)
  • Possibilità di un recupero completo e inverso dei metadati in caso di perdita totale (nel caso di utilizzo del distributore)
  • Protocollo nativo per la possibilità di utilizzare connessioni di rete permanenti e driver per diversi linguaggi di programmazione
  • Maggiore utilizzo delle funzionalità NoSQL
  • Compressioni di vari tipi (gzip, zstd, snappy) per file o valori all'interno degli archivi Bolt e per file normali
  • Crittografia di vari tipi per file o valori all'interno degli archivi Bolt e per file normali
  • Conversione video server ritardata, inclusa quella su GPU

Ho finito, spero che questo server possa essere utile a qualcuno, licenza BSD-3, copyright doppio, poiché non ci sarebbe stata l'azienda in cui lavoro, non avrei scritto il server. Sono un sviluppatore singolo. Sarò grato per bug trovati e richieste di funzionalità.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster