Små filer är ett stort problem i Hadoop - eller åtminstone om antalet frågor på användarlistan om detta ämne är något att gå efter. I det här inlägget ska jag titta på problemet och undersöka några vanliga lösningar.
Problem med små filer och HDFS
En liten fil är en som är betydligt mindre än HDFS-blockstorleken (standard 64MB). Om du lagrar små filer har du förmodligen många av dem (annars skulle du inte vända dig till Hadoop), och problemet är att HDFS inte kan hantera många filer.
Varje fil, katalog och block i HDFS representeras som ett objekt i namnnodens minne, som var och en upptar 150 byte, som en tumregel. Så 10 miljoner filer, som var och en använder ett block, skulle använda cirka 3 gigabyte minne. Att skala upp mycket över denna nivå är ett problem med nuvarande hårdvara. Visst är en miljard filer inte genomförbart.
Dessutom är HDFS inte inriktat på att effektivt komma åt små filer:det är främst designat för streaming åtkomst av stora filer. Att läsa igenom små filer orsakar normalt många sökningar och massor av hopp från datanod till datanod för att hämta varje liten fil, vilket allt är ett ineffektivt dataåtkomstmönster.
Problem med små filer och MapReduce
Kartuppgifter behandlar vanligtvis ett indatablock åt gången (med standard FileInputFormat
). Om filen är mycket liten och det finns många av dem, bearbetar varje kartuppgift väldigt lite indata, och det finns många fler kartuppgifter, som var och en medför extra bokföringskostnader. Jämför en 1GB-fil uppdelad i 16 64MB-block och 10 000 eller så 100KB-filer. De 10 000 filerna använder en karta var och jobbtiden kan vara tiotals eller hundratals gånger långsammare än motsvarande med en enda indatafil.
Det finns ett par funktioner som hjälper till att lindra bokföringskostnaderna:återanvändning av uppgift JVM för att köra flera kartuppgifter i en JVM, och därigenom undvika vissa JVM-startoverhead (se mapred.job.reuse.jvm.num.tasks
kod> egenskap) och MultiFileInputSplit
som kan köra mer än en delning per karta.
Varför produceras små filer?
Det finns minst två fall
- Filerna är delar av en större logisk fil. Eftersom HDFS bara nyligen har stöd för tillägg, är ett mycket vanligt mönster för att spara obegränsade filer (t.ex. loggfiler) att skriva dem i bitar i HDFS.
- Filerna är till sin natur små. Föreställ dig en stor bildsamling. Varje bild är en distinkt fil, och det finns inget naturligt sätt att kombinera dem till en större fil.
Dessa två fall kräver olika lösningar. I det första fallet, där filen består av poster, kan problemet undvikas genom att anropa HDFS:s sync()
metod med jämna mellanrum för att kontinuerligt skriva stora filer. Alternativt är det möjligt att skriva ett program för att sammanfoga de små filerna.
För det andra fallet behövs någon form av behållare för att gruppera filerna på något sätt. Hadoop erbjuder några alternativ här.
HAR-filer
Hadoop Archives (HAR-filer) introducerades till HDFS i 0.18.0 för att lindra problemet med massor av filer som satte press på namnnodens minne. HAR-filer fungerar genom att bygga ett skiktat filsystem ovanpå HDFS. En HAR-fil skapas med hjälp av hadoop archive
kommando, som kör ett MapReduce-jobb för att packa filerna som arkiveras i ett litet antal HDFS-filer. För en klient som använder HAR-filsystemet har ingenting förändrats:alla originalfiler är synliga och tillgängliga (om än med en har:// URL). Däremot har antalet filer i HDFS minskat.
Att läsa igenom filer i en HAR är inte effektivare än att läsa igenom filer i HDFS, och det kan faktiskt vara långsammare eftersom varje HAR-filåtkomst kräver två indexfilläsningar samt datafilen som läses (se diagram). Och även om HAR-filer kan användas som indata till MapReduce, finns det ingen speciell magi som tillåter kartor att fungera över alla filer i HAR-medboendet på ett HDFS-block. Det borde vara möjligt att bygga ett indataformat som kan dra fördel av den förbättrade lokaliteten för filer i HAR, men det finns inte ännu. Observera att MultiFileInputSplit, även med förbättringarna i HADOOP-4565 för att välja filer i en uppdelning som är nodlokal, kommer att behöva en sökning per liten fil. Det skulle vara intressant att se prestandan för detta jämfört med en SequenceFile, säg. För närvarande är det förmodligen bäst att använda HAR enbart för arkivändamål.
Sekvensfiler
Det vanliga svaret på frågor om "problemet med små filer" är:använd en SequenceFile. Tanken här är att du använder filnamnet som nyckel och filens innehåll som värde. Detta fungerar mycket bra i praktiken. Om du går tillbaka till de 10 000 100KB-filerna kan du skriva ett program för att lägga dem i en enda SequenceFile, och sedan kan du bearbeta dem på ett strömmande sätt (direkt eller med hjälp av MapReduce) som fungerar på SequenceFile. Det finns ett par bonusar också. SequenceFiles är delbara, så MapReduce kan dela upp dem i bitar och arbeta på varje bit oberoende. De stöder också komprimering, till skillnad från HAR. Blockkomprimering är det bästa alternativet i de flesta fall, eftersom det komprimerar block med flera poster (snarare än per post).
Det kan vara långsamt att konvertera befintliga data till SequenceFiles. Det är dock fullt möjligt att skapa en samling SequenceFiles parallellt. (Stuart Sierra har skrivit ett mycket användbart inlägg om att konvertera en tar-fil till en SequenceFile — verktyg som detta är mycket användbara, och det skulle vara bra att se fler av dem). Framöver är det bäst att utforma din datapipeline för att skriva data vid källan direkt i en SequenceFile, om möjligt, snarare än att skriva till små filer som ett mellansteg.
Till skillnad från HAR-filer finns det inget sätt att lista alla nycklar i en SequenceFile, utan att läsa igenom hela filen. (MapFiles, som är som SequenceFiles med sorterade nycklar, upprätthåller ett partiellt index, så att de inte heller kan lista alla sina nycklar – se diagram.)
SequenceFile är ganska Java-centrerad. TFile är utformad för att vara plattformsoberoende och vara en ersättning för SequenceFile, men den är inte tillgänglig än.
HBase
Om du producerar många små filer kan en annan typ av lagring vara lämpligare beroende på åtkomstmönstret. HBase lagrar data i MapFiles (indexerade SequenceFiles), och är ett bra val om du behöver göra strömningsanalyser i MapReduce-stil med en och annan slumpmässig uppslagning. Om latens är ett problem, så finns det många andra val – se Richard Jones utmärkta undersökning av nyckel-värde butiker.