Kleine bestanden zijn een groot probleem in Hadoop - of dat zijn ze tenminste als het aantal vragen op de gebruikerslijst over dit onderwerp iets is om af te gaan. In dit bericht zal ik het probleem bekijken en enkele veelvoorkomende oplossingen onderzoeken.
Problemen met kleine bestanden en HDFS
Een klein bestand is een bestand dat aanzienlijk kleiner is dan de HDFS-blokgrootte (standaard 64 MB). Als je kleine bestanden opslaat, heb je er waarschijnlijk veel (anders zou je Hadoop niet gebruiken), en het probleem is dat HDFS niet veel bestanden aankan.
Elk bestand, elke map en elk blok in HDFS wordt weergegeven als een object in het geheugen van namenode, dat elk 150 bytes in beslag neemt, als vuistregel. Dus 10 miljoen bestanden, die elk een blok gebruiken, zouden ongeveer 3 gigabyte geheugen gebruiken. Veel verder opschalen dan dit niveau is een probleem met de huidige hardware. Een miljard bestanden is zeker niet haalbaar.
Bovendien is HDFS niet afgestemd op het efficiënt benaderen van kleine bestanden:het is primair ontworpen voor streamingtoegang tot grote bestanden. Het doorlezen van kleine bestanden veroorzaakt normaal gesproken veel zoekacties en veel hoppen van datanode naar datanode om elk klein bestand op te halen, wat allemaal een inefficiënt gegevenstoegangspatroon is.
Problemen met kleine bestanden en MapReduce
Kaarttaken verwerken meestal een invoerblok tegelijk (met behulp van de standaard FileInputFormat
). Als het bestand erg klein is en er zijn er veel, dan verwerkt elke kaarttaak heel weinig invoer en zijn er veel meer kaarttaken, die elk extra boekhoudkundige overhead met zich meebrengen. Vergelijk een bestand van 1 GB dat is opgedeeld in 16 blokken van 64 MB en ongeveer 10.000 bestanden van 100 KB. De 10.000 bestanden gebruiken elk één kaart en de taaktijd kan tientallen of honderden keren langzamer zijn dan de equivalente met een enkel invoerbestand.
Er zijn een aantal functies die de overhead van de boekhouding helpen verlichten:hergebruik van taak-JVM voor het uitvoeren van meerdere kaarttaken in één JVM, waardoor enige JVM-opstartoverhead wordt vermeden (zie de mapred.job.reuse.jvm.num.tasks
code> eigenschap), en MultiFileInputSplit
die meer dan één split per kaart kan uitvoeren.
Waarom worden er kleine bestanden gemaakt?
Er zijn minstens twee gevallen
- De bestanden zijn stukjes van een groter logisch bestand. Aangezien HDFS pas sinds kort appends ondersteunt, is een veelgebruikt patroon voor het opslaan van onbegrensde bestanden (bijv. logbestanden) om ze in brokken naar HDFS te schrijven.
- De bestanden zijn van nature klein. Stel je een groot corpus aan afbeeldingen voor. Elke afbeelding is een apart bestand en er is geen natuurlijke manier om ze te combineren tot één groter bestand.
Deze twee gevallen vragen om verschillende oplossingen. In het eerste geval, waar het bestand uit records bestaat, kan het probleem worden vermeden door sync()
van HDFS aan te roepen. methode om af en toe grote bestanden te schrijven. Als alternatief is het mogelijk om een programma te schrijven om de kleine bestanden samen te voegen.
Voor het tweede geval is een soort container nodig om de bestanden op de een of andere manier te groeperen. Hadoop biedt hier een paar opties.
HAR-bestanden
Hadoop-archieven (HAR-bestanden) werden geïntroduceerd in HDFS in 0.18.0 om het probleem te verlichten van veel bestanden die druk uitoefenen op het geheugen van de namenode. HAR-bestanden werken door een gelaagd bestandssysteem te bouwen bovenop HDFS. Er wordt een HAR-bestand gemaakt met behulp van het hadoop archive
commando, dat een MapReduce-taak uitvoert om de bestanden die worden gearchiveerd in een klein aantal HDFS-bestanden in te pakken. Voor een klant die het HAR-bestandssysteem gebruikt, is er niets veranderd:alle originele bestanden zijn zichtbaar en toegankelijk (zij het met een har:// URL). Het aantal bestanden in HDFS is echter verminderd.
Het doorlezen van bestanden in een HAR is niet efficiënter dan het doorlezen van bestanden in HDFS, en kan in feite langzamer zijn omdat voor elke toegang tot het HAR-bestand twee leesbewerkingen van het indexbestand nodig zijn, evenals het lezen van het gegevensbestand (zie diagram). En hoewel HAR-bestanden kunnen worden gebruikt als invoer voor MapReduce, is er geen speciale magie waarmee kaarten kunnen werken over alle bestanden in de HAR-co-resident op een HDFS-blok. Het zou mogelijk moeten zijn om een invoerformaat te bouwen dat kan profiteren van de verbeterde lokaliteit van bestanden in HAR's, maar het bestaat nog niet. Merk op dat MultiFileInputSplit, zelfs met de verbeteringen in HADOOP-4565 om bestanden in een splitsing te kiezen die lokaal knooppunt zijn, een zoekactie per klein bestand nodig heeft. Het zou interessant zijn om de prestaties hiervan te zien in vergelijking met bijvoorbeeld een SequenceFile. Op dit moment kunnen HAR's waarschijnlijk het beste puur voor archiveringsdoeleinden worden gebruikt.
Bestanden Volgorde
Het gebruikelijke antwoord op vragen over "het probleem met kleine bestanden" is:gebruik een SequenceFile. Het idee hier is dat je de bestandsnaam als sleutel en de bestandsinhoud als waarde gebruikt. Dit werkt in de praktijk erg goed. Terugkerend naar de 10.000 bestanden van 100 KB, kun je een programma schrijven om ze in een enkele SequenceFile te plaatsen, en dan kun je ze op een streaming-manier verwerken (rechtstreeks of met behulp van MapReduce) die op de SequenceFile werkt. Er zijn ook een paar bonussen. SequenceFiles zijn splitsbaar, dus MapReduce kan ze in stukken opsplitsen en op elk stuk afzonderlijk werken. Ze ondersteunen ook compressie, in tegenstelling tot HAR's. Blokcompressie is in de meeste gevallen de beste optie, omdat het blokken van meerdere records comprimeert (in plaats van per record).
Het kan traag zijn om bestaande gegevens om te zetten in SequenceFiles. Het is echter perfect mogelijk om parallel een verzameling SequenceFiles aan te maken. (Stuart Sierra heeft een zeer nuttige post geschreven over het converteren van een tar-bestand naar een SequenceFile - tools zoals deze zijn erg handig, en het zou goed zijn om er meer van te zien). In de toekomst is het het beste om uw gegevenspijplijn zo te ontwerpen dat de gegevens bij de bron rechtstreeks naar een SequenceFile worden geschreven, indien mogelijk, in plaats van als tussenstap naar kleine bestanden te schrijven.
In tegenstelling tot HAR-bestanden is er geen manier om alle sleutels in een SequenceFile op te sommen, behalve het doorlezen van het hele bestand. (MapFiles, die lijken op SequenceFiles met gesorteerde sleutels, houden een gedeeltelijke index bij, zodat ze ook niet al hun sleutels kunnen weergeven - zie diagram.)
SequenceFile is nogal Java-centrisch. TFile is ontworpen om platformonafhankelijk te zijn en een vervanging te zijn voor SequenceFile, maar het is nog niet beschikbaar.
HBase
Als u veel kleine bestanden produceert, kan, afhankelijk van het toegangspatroon, een ander type opslag geschikter zijn. HBase slaat gegevens op in MapFiles (geïndexeerde SequenceFiles) en is een goede keuze als u streaminganalyses in MapReduce-stijl moet uitvoeren met af en toe een willekeurige zoekopdracht. Als latentie een probleem is, zijn er tal van andere keuzes - zie Richard Jones' uitstekende onderzoek naar winkels met een sleutelwaarde.