Inleiding
Apache HBase is de Hadoop open-source, gedistribueerde opslagmanager met versiebeheer die zeer geschikt is voor willekeurige , realtime lezen/schrijven toegang.
Wacht wacht? willekeurige, realtime lees-/schrijftoegang?
Hoe is dat mogelijk? Is Hadoop niet gewoon een sequentieel lees-/schrijfsysteem voor batchverwerking?
Ja, we hebben het over hetzelfde, en in de volgende paragrafen ga ik je uitleggen hoe HBase de willekeurige I/O bereikt, hoe het gegevens opslaat en de evolutie van het HFile-formaat van de HBase.
Apache Hadoop I/O-bestandsindelingen
Hadoop wordt geleverd met een SequenceFile[1]-bestandsindeling die u kunt gebruiken om uw sleutel/waarde-paren toe te voegen, maar vanwege de mogelijkheid om alleen hdfs toe te voegen, kan de bestandsindeling geen wijziging of verwijdering van een ingevoegde waarde toestaan. De enige toegestane bewerking is toevoegen, en als u een opgegeven sleutel wilt opzoeken, moet u het bestand doorlezen totdat u uw sleutel vindt.
Zoals je kunt zien, ben je gedwongen het sequentiële lees-/schrijfpatroon te volgen... maar hoe is het mogelijk om daarbovenop een willekeurig lees-/schrijftoegangssysteem met lage latentie zoals HBase te bouwen?
Om u te helpen dit probleem op te lossen, heeft Hadoop een ander bestandsformaat, MapFile[1] genaamd, een extensie van het SequenceFile. De MapFile is in werkelijkheid een map die twee SequenceFiles bevat:het gegevensbestand "/data" en het indexbestand "/index". Met de MapFile kunt u gesorteerde sleutel/waarde-paren toevoegen en elke N-sleutel (waarbij N een configureerbaar interval is) slaat de sleutel en de offset op in de index. Dit zorgt voor een vrij snelle opzoeking, omdat in plaats van alle records te scannen, u de index scant die minder items bevat. Zodra je je blok hebt gevonden, kun je in het echte gegevensbestand springen.
MapFile is leuk omdat je snel sleutel/waarde-paren kunt opzoeken, maar er zijn nog steeds twee problemen:
- Hoe kan ik een sleutel/waarde verwijderen of vervangen?
- Als mijn invoer niet is gesorteerd, kan ik MapFile niet gebruiken.
HBase &MapFile
De HBase-sleutel bestaat uit:de rijsleutel, kolomfamilie, kolomkwalificatie, tijdstempel en een type.
Om het probleem van het verwijderen van sleutel/waarde-paren op te lossen, is het de bedoeling om het veld "type" te gebruiken om de sleutel als verwijderd te markeren (grafsteenmarkeringen). Het oplossen van het probleem van het vervangen van sleutel/waarde-paren is gewoon een kwestie van het latere tijdstempel kiezen (de juiste waarde staat aan het einde van het bestand, alleen toevoegen betekent dat de laatst ingevoegde waarde bijna aan het einde is).
Om het "niet-geordende" sleutelprobleem op te lossen, bewaren we de laatst toegevoegde sleutel-waarden in het geheugen. Wanneer u een drempel hebt bereikt, spoelt HBase deze door naar een MapFile. Op deze manier voegt u uiteindelijk gesorteerde sleutel/waarden toe aan een MapFile.
HBase doet precies dit [2]:wanneer je een waarde toevoegt met table.put(), wordt je sleutel/waarde toegevoegd aan de MemStore (onder de motorkap is MemStore een gesorteerde ConcurrentSkipListMap). Wanneer de drempel per geheugenopslag (hbase.hregion.memstore.flush.size) is bereikt of de RegionServer te veel geheugen gebruikt voor geheugenopslag (hbase.regionserver.global.memstore.upperLimit), worden gegevens op schijf leeggemaakt als een nieuwe MapFile .
Het resultaat van elke flush is een nieuwe MapFile, en dit betekent dat u in meer dan één bestand moet zoeken om een sleutel te vinden. Dit kost meer middelen en is mogelijk langzamer.
Elke keer dat een get of een scan wordt uitgevoerd, scant HBase door elk bestand om het resultaat te vinden, om te voorkomen dat u door te veel bestanden heen springt, is er een thread die detecteert wanneer u een bepaald aantal bestanden hebt bereikt (hbase.hstore.compaction .max). Vervolgens probeert het ze samen te voegen in een proces dat compactie wordt genoemd, dat in feite een nieuw groot bestand maakt als resultaat van het samenvoegen van bestanden.
HBase heeft twee soorten verdichting:de ene genaamd "kleine verdichting" die twee of meer kleine bestanden samenvoegt in één, en de andere genaamd "grote verdichting" die alle bestanden in de regio oppikt, ze samenvoegt en enige opschoning uitvoert. Bij een grote verdichting worden verwijderde sleutel/waarden verwijderd, dit nieuwe bestand bevat niet de tombstone-markeringen en alle dubbele sleutel/waarden (vervangingsbewerkingen) worden verwijderd.
Tot versie 0.20 heeft HBase het MapFile-formaat gebruikt om de gegevens op te slaan, maar in 0.20 werd een nieuwe HBase-specifieke MapFile geïntroduceerd (HBASE-61).
HFile v1
In HBase 0.20 is MapFile vervangen door HFile:een specifieke implementatie van mapbestanden voor HBase. Het idee lijkt veel op MapFile, maar het voegt meer functies toe dan alleen een gewoon sleutel-/waardebestand. Functies zoals ondersteuning voor metadata en de index worden nu in hetzelfde bestand bewaard.
De datablokken bevatten de actuele sleutel/waarden als MapFile. Voor elke "block close operatie" wordt de eerste sleutel toegevoegd aan de index, en de index wordt geschreven op HFile close.
Het HFile-formaat voegt ook twee extra "metadata" -bloktypen toe:Meta en FileInfo. Deze twee sleutel/waarde-blokken worden geschreven bij het sluiten van het bestand.
Het Meta-blok is ontworpen om een grote hoeveelheid gegevens te bewaren met zijn sleutel als een tekenreeks, terwijl FileInfo een eenvoudige kaart is die de voorkeur heeft voor kleine informatie met sleutels en waarden die beide byte-array zijn. StoreFile van Regionserver gebruikt Meta-Blocks om een Bloom Filter op te slaan, en FileInfo voor Max SequenceId, Major compaction key en Timerange info. Deze informatie is handig om te voorkomen dat het bestand wordt gelezen als er geen kans is dat de sleutel aanwezig is (Bloom Filter), als het bestand te oud is (Max SequenceId) of als het bestand te nieuw is (Tijdbereik) om te bevatten wat we zoeken voor.
HFile v2
In HBase 0.92 is het HFile-formaat een beetje gewijzigd (HBASE-3857) om de prestaties te verbeteren wanneer grote hoeveelheden gegevens worden opgeslagen. Een van de belangrijkste problemen met de HFile v1 is dat je alle monolithische indexen en grote Bloom Filters in het geheugen moet laden, en om dit probleem op te lossen introduceert v2 indexen op meerdere niveaus en een Bloom Filter op blokniveau. Als gevolg hiervan biedt HFile v2 een verbeterde snelheid, geheugen en cachegebruik.
Het belangrijkste kenmerk van deze v2 zijn "inline-blokken", het idee is om de index en Bloom Filter per blok te breken, in plaats van de hele index en Bloom Filter van het hele bestand in het geheugen te hebben. Op deze manier kun je in ram precies houden wat je nodig hebt.
Omdat de index naar blokniveau wordt verplaatst, heb je een index met meerdere niveaus, wat betekent dat elk blok zijn eigen index heeft (bladindex). De laatste sleutel van elk blok wordt bewaard om de intermediate/index te creëren die de multilevel-index b+tree leuk maakt.
De block header bevat nu wat informatie:Het “Block Magic” veld is vervangen door het “Block Type” veld dat de inhoud beschrijft van het block “Data”, Leaf-Index, Bloom, Metadata, Root-Index, etc. Ook drie velden (gecomprimeerde/ongecomprimeerde grootte en offset vorig blok) zijn toegevoegd om snel achteruit en vooruit te zoeken.
Datablokcoderingen
Omdat sleutels gesorteerd zijn en meestal erg op elkaar lijken, is het mogelijk om een betere compressie te ontwerpen dan een algoritme voor algemeen gebruik kan doen.
HBASE-4218 heeft geprobeerd dit probleem op te lossen en in HBase 0.94 kun je kiezen uit een aantal verschillende algoritmen:Prefix en Diff Encoding.
Het belangrijkste idee van Prefix Encoding is om het algemene voorvoegsel slechts één keer op te slaan, aangezien de rijen zijn gesorteerd en het begin meestal hetzelfde is.
De Diff Encoding duwt dit concept verder. In plaats van de sleutel te beschouwen als een ondoorzichtige reeks bytes, splitst de Diff Encoder elk sleutelveld om elk onderdeel op een betere manier te comprimeren. Dit is dat de kolomfamilie één keer wordt opgeslagen. Als de sleutellengte, waardelengte en type hetzelfde zijn als de voorgaande rij, wordt het veld weggelaten. Voor verhoogde compressie wordt de tijdstempel ook opgeslagen als een Diff van de vorige.
Houd er rekening mee dat deze functie standaard is uitgeschakeld, omdat schrijven en scannen langzamer zijn, maar er worden meer gegevens in de cache opgeslagen. Om deze functie in te schakelen, kunt u DATA_BLOCK_ENCODING =PREFIX | . instellen VERSCHIL | FAST_DIFF in de tabelinfo.
HFile v3
HBASE-5313 bevat een voorstel om de HFile-lay-out te herstructureren om de compressie te verbeteren:
- Pak alle sleutels samen aan het begin van het blok en alle waarden samen aan het einde van het blok. Op deze manier kunt u twee verschillende algoritmen gebruiken om sleutel en waarden te comprimeren.
- Comprimeer tijdstempels met de XOR met de eerste waarde en gebruik VInt in plaats van lang.
Er wordt ook onderzoek gedaan naar een zuilvormig formaat of een zuilvormige codering, kijk op AVRO-806 voor een zuilvormig bestandsformaat door Doug Cutting.
Zoals je misschien ziet, is de trend in evolutie om meer bewust te zijn van wat het bestand bevat, om betere compressie of beter locatiebewustzijn te krijgen, wat zich vertaalt in minder gegevens om van schijf te schrijven/lezen. Minder I/O betekent meer snelheid!
[1] https://clouderatemp.wpengine.com/blog/2011/01/hadoop-io-sequence-map-set-array-bloommap-files/
[2] https://clouderatemp.wpengine. com/blog/2012/06/hbase-write-path/