Introduktion
Apache HBase är Hadoop öppen källkod, distribuerad, versionerad lagringshanterare väl lämpad för slumpmässiga , läs/skriv i realtid åtkomst.
Vänta vänta? slumpmässig läs-/skrivåtkomst i realtid?
Hur är det möjligt? Är inte Hadoop bara ett sekventiellt läs/skriv, batchbearbetningssystem?
Ja, vi pratar om samma sak, och i de kommande styckena kommer jag att förklara för dig hur HBase uppnår den slumpmässiga I/O, hur den lagrar data och utvecklingen av HBases HFile-format.
Apache Hadoop I/O-filformat
Hadoop kommer med ett SequenceFile[1]-filformat som du kan använda för att lägga till dina nyckel-/värdepar, men på grund av hdfs-funktionen för endast tillägg kan filformatet inte tillåta modifiering eller borttagning av ett infogat värde. Den enda tillåtna operationen är att lägga till, och om du vill slå upp en angiven nyckel måste du läsa igenom filen tills du hittar din nyckel.
Som du kan se är du tvungen att följa det sekventiella läs/skrivmönstret... men hur är det möjligt att bygga ett slumpmässigt läs-/skrivåtkomstsystem med låg latens som HBase ovanpå detta?
För att hjälpa dig att lösa detta problem har Hadoop ett annat filformat, kallat MapFile[1], en förlängning av SequenceFile. MapFile är i själva verket en katalog som innehåller två SequenceFiles:datafilen "/data" och indexfilen "/index". MapFile låter dig lägga till sorterade nyckel/värdepar och varje N nycklar (där N är ett konfigurerbart intervall) lagrar nyckeln och offseten i indexet. Detta möjliggör en ganska snabb sökning, eftersom du istället för att skanna alla poster skannar indexet som har färre poster. När du har hittat ditt block kan du hoppa in i den riktiga datafilen.
MapFile är bra eftersom du kan slå upp nyckel/värdepar snabbt men det finns fortfarande två problem:
- Hur kan jag ta bort eller ersätta en nyckel/värde?
- När min inmatning inte är sorterad kan jag inte använda MapFile.
HBase &MapFile
HBase Key består av:radnyckeln, kolumnfamiljen, kolumnkvalificeraren, tidsstämpeln och en typ.
För att lösa problemet med att ta bort nyckel-/värdepar är tanken att använda "typ"-fältet för att markera nyckel som borttagen (gravstensmarkörer). Att lösa problemet med att ersätta nyckel-/värdepar är bara en fråga om att välja den senare tidsstämpeln (det korrekta värdet är nära slutet av filen, bara lägga till betyder att senast infogade är nära slutet).
För att lösa det "icke-ordnade" nyckelproblemet behåller vi de senast tillagda nyckel-värdena i minnet. När du har nått en tröskel spolar HBase den till en MapFile. På detta sätt lägger du till sorterade nyckel/värden till en MapFile.
HBase gör exakt detta[2]:när du lägger till ett värde med table.put(), läggs din nyckel/värde till MemStore (under huven MemStore finns en sorterad ConcurrentSkipListMap). När tröskeln per memstore (hbase.hregion.memstore.flush.size) nås eller RegionServer använder för mycket minne för memstore (hbase.regionserver.global.memstore.upperLimit), töms data på disken som en ny MapFile .
Resultatet av varje spolning är en ny MapFile, och det betyder att för att hitta en nyckel måste du söka i mer än en fil. Detta tar mer resurser och är potentiellt långsammare.
Varje gång en get eller en skanning utfärdas, skanna HBase igenom varje fil för att hitta resultatet, för att undvika att hoppa runt för många filer, det finns en tråd som kommer att upptäcka när du har nått ett visst antal filer (hbase.hstore.compaction .max). Den försöker sedan slå samman dem i en process som kallas komprimering, som i princip skapar en ny stor fil som ett resultat av filsammanfogningen.
HBase har två typer av komprimering:en som kallas "mindre komprimering" som bara slår samman två eller flera små filer till en, och den andra kallas "stor komprimering" som plockar upp alla filer i regionen, slår samman dem och utför en viss rensning. I en större komprimering tas borttagna nyckel/värden bort, den här nya filen innehåller inte gravstensmarkörerna och alla dubbletter av nyckel/värden (ersätt värdeoperationer) tas bort.
Fram till version 0.20 har HBase använt MapFile-formatet för att lagra data men i 0.20 introducerades en ny HBase-specifik MapFile (HBASE-61).
HF-fil v1
I HBase 0.20 ersätts MapFile med HFile:en specifik kartfilimplementering för HBase. Idén är ganska lik MapFile, men den lägger till fler funktioner än bara en vanlig nyckel-/värdefil. Funktioner som stöd för metadata och index finns nu i samma fil.
Datablocken innehåller de faktiska nyckeln/värdena som en MapFile. För varje "block stängningsoperation" läggs den första nyckeln till i indexet, och indexet skrivs på HFile close.
HFile-formatet lägger också till två extra "metadata"-blocktyper:Meta och FileInfo. Dessa två nyckel-/värdeblock skrivs när filen stängs.
Metablocket är utformat för att behålla en stor mängd data med dess nyckel som en sträng, medan FileInfo är en enkel karta som föredras för liten information med nycklar och värden som båda är byte-array. Regionservers StoreFile använder Meta-Blocks för att lagra ett Bloom-filter och FileInfo för Max SequenceId, större komprimeringsnyckel och tidsintervallsinformation. Den här informationen är användbar för att undvika att läsa filen om det inte finns någon chans att nyckeln finns (Bloom Filter), om filen är för gammal (Max SequenceId) eller om filen är för ny (Tidsintervall) för att innehålla det vi letar efter för.
HFile v2
I HBase 0.92 ändrades HFile-formatet lite (HBASE-3857) för att förbättra prestandan när stora mängder data lagras. Ett av huvudproblemen med HFile v1 är att du måste ladda alla monolitiska index och stora Bloom-filter i minnet, och för att lösa detta problem introducerar v2 index på flera nivåer och ett Bloom-filter på blocknivå. Som ett resultat har HFile v2 förbättrad hastighet, minne och cacheanvändning.
Huvudfunktionen i denna v2 är "inline block", tanken är att bryta indexet och Bloom Filter per block, istället för att ha hela indexet och Bloom Filter för hela filen i minnet. På så sätt kan du ha i ram precis vad du behöver.
Eftersom indexet flyttas till blocknivå har du ett flernivåindex, vilket innebär att varje block har sitt eget index (bladindex). Den sista nyckeln i varje block behålls för att skapa det mellanliggande/index som gör multilevel-index b+tree-liknande.
Blockhuvudet innehåller nu lite information:Fältet "Block Magic" ersattes av fältet "Block Type" som beskriver innehållet i blocket "Data", Leaf-Index, Bloom, Metadata, Root-Index, etc. Även tre fält (komprimerad/okomprimerad storlek och offset prev block) lades till för att möjliggöra snabba sökningar bakåt och framåt.
Datablockkodningar
Eftersom nycklar är sorterade och vanligtvis väldigt lika, är det möjligt att designa en bättre komprimering än vad en generell algoritm kan göra.
HBASE-4218 försökte lösa detta problem, och i HBase 0.94 kan du välja mellan ett par olika algoritmer:Prefix och Diff Encoding.
Huvudidén med prefixkodning är att lagra det vanliga prefixet endast en gång, eftersom raderna är sorterade och början vanligtvis är densamma.
Diff Encoding driver detta koncept ytterligare. Istället för att betrakta nyckeln som en ogenomskinlig sekvens av bytes, delar Diff Encoder upp varje nyckelfält för att komprimera varje del på ett bättre sätt. Detta är att kolumnfamiljen lagras en gång. Om nyckellängden, värdelängden och typen är samma som raden innan, utelämnas fältet. Dessutom, för ökad komprimering, lagras tidsstämpeln som en skillnad från den föregående.
Observera att den här funktionen är avstängd som standard eftersom skrivning och skanning går långsammare men mer data cachelagras. För att aktivera den här funktionen kan du ställa in DATA_BLOCK_ENCODING =PREFIX | DIFF | FAST_DIFF i tabellinformationen.
HF-fil v3
HBASE-5313 innehåller ett förslag att omstrukturera HFile-layouten för att förbättra komprimeringen:
- Packa alla nycklar i början av blocket och alla värden tillsammans i slutet av blocket. På detta sätt kan du använda två olika algoritmer för att komprimera nyckel och värden.
- Komprimera tidsstämplar med XOR med det första värdet och använd VInt istället för long.
Dessutom är ett kolumnformat eller en kolumnär kodning under utredning, ta en titt på AVRO-806 för ett kolumnformat filformat av Doug Cutting.
Som du kanske ser är trenden i utvecklingen att vara mer medveten om vad filen innehåller, för att få bättre komprimering eller bättre platsmedvetenhet som leder till mindre data att skriva/läsa från disk. Mindre I/O betyder högre hastighet!
[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/