sql >> Database teknologi >  >> NoSQL >> HBase

HBase BlockCache 101

Dette blogindlæg blev offentliggjort på Hortonworks.com før fusionen med Cloudera. Nogle links, ressourcer eller referencer er muligvis ikke længere nøjagtige.

Dette blogindlæg dukkede oprindeligt op her og er gengivet i sin helhed her.

HBase er en distribueret database bygget op omkring kernekoncepterne for en ordnet skrivelog og et logstruktureret flettetræ. Som med enhver database er optimeret I/O en kritisk bekymring for HBase. Når det er muligt, prioriteres det ikke at udføre nogen I/O overhovedet. Dette betyder, at hukommelsesudnyttelse og cachestrukturer er af yderste vigtighed. Til dette formål opretholder HBase to cache-strukturer:"hukommelseslageret" og "blokcachen". Hukommelseslager, implementeret som MemStore , akkumulerer dataredigeringer, efterhånden som de modtages, og buffer dem i hukommelsen (1). Blokcachen, en implementering af BlockCache interface, holder datablokke i hukommelsen, efter de er læst.

 MemStore er vigtig for at få adgang til de seneste redigeringer. Uden MemStore , at få adgang til disse data, som de blev skrevet ind i skriveloggen, ville kræve læsning og deserialisering af poster tilbage fra den fil, mindst en O(n) operation. I stedet MemStore opretholder en skiplistestruktur, som har en O(log n) adgangsomkostninger og kræver ingen disk I/O. MemStore indeholder dog kun et lille stykke af de data, der er gemt i HBase.

Service læser fra BlockCache er den primære mekanisme, hvorigennem HBase er i stand til at betjene tilfældige læsninger med millisekunders latency. Når en datablok læses fra HDFS, cachelagres den i BlockCache . Efterfølgende læsninger af tilstødende data – data fra samme blok – lider ikke I/O-straffen ved igen at hente disse data fra disken (2). Det er BlockCache det vil være det resterende fokus i dette indlæg.

Blokerer til cache

Før du forstår BlockCache , hjælper det at forstå, hvad præcis en HBase "blok" er. I HBase-sammenhæng er en blok en enkelt enhed af I/O. Når du skriver data ud til en HF-fil, er blokken den mindste enhed af data, der er skrevet. Ligeledes er en enkelt blok den mindste mængde data, HBase kan læse tilbage fra en HF-fil. Pas på ikke at forveksle en HBase-blok med en HDFS-blok eller med blokkene i det underliggende filsystem – disse er alle forskellige (3).

HBase-blokke findes i 4 varianter: DATAMETAINDEX , og BLOOM .

DATA blokerer gemmer brugerdata. Når BLOCKSIZE er angivet for en kolonnefamilie, er det et tip til denne type blok. Husk, det er kun et tip. Mens du skyller MemStore , vil HBase gøre sit bedste for at overholde denne retningslinje. Efter hver celle er skrevet, kontrollerer forfatteren, om det skrevne beløb er>=målet BLOCKSIZE . Hvis det er tilfældet, lukker den den aktuelle blok og starter den næste (4).

INDEX og BLOOM blokke tjener det samme mål; begge bruges til at fremskynde læsestien. INDEX blokke giver et indeks over cellen s indeholdt i DATA blokke. BLOOM blokke indeholder et opblomstringsfilter over de samme data. Indekset giver læseren mulighed for hurtigt at vide, hvor en celle skal opbevares. Filteret fortæller læseren, når en celle er absolut fraværende i dataene.

Til sidst META blokke gemmer information om selve HF-filen og anden diverse information – metadata, som du måske forventer. En mere omfattende oversigt over HFile-formaterne og rollerne for forskellige bloktyper findes i Apache HBase I/O – HFile.

HBase BlockCache og dens implementeringer

Der er en enkelt BlockCache instans i en regionsserver, hvilket betyder, at alle data fra alle regioner, der hostes af den server, deler den samme cache-pulje (5). BlockCache instansieres ved opstart af regionsserver og bevares i hele processens levetid. Traditionelt leverede HBase kun en enkelt BlockCache implementering: LruBlockCache . 0.92-udgivelsen introducerede det første alternativ i HBASE-4027: SlabCache . HBase 0.96 introducerede en anden mulighed via HBASE-7404, kaldet BucketCache .

Den vigtigste forskel mellem den gennemprøvede LruBlockCache og disse alternativer er måden, de håndterer hukommelsen på. Nærmere bestemt LruBlockCache er en datastruktur, der udelukkende ligger på JVM-heapen, mens de to andre er i stand til at udnytte hukommelsen uden for JVM-heapen. Dette er en vigtig forskel, fordi JVM-bunkehukommelsen administreres af JVM Garbage Collector, mens de andre ikke er det. I tilfælde af SlabCache og BucketCache , er ideen at reducere GC-trykket, som regionens serverproces oplever, ved at reducere antallet af objekter, der tilbageholdes på heapen.

LruBlockCache

Dette er standardimplementeringen. Datablokke cachelagres i JVM-heap ved hjælp af denne implementering. Det er opdelt i tre områder:enkelt adgang, multiadgang og in-memory. Områderne er dimensioneret til 25 %, 50 %, 25 % af den samlede BlockCache henholdsvis størrelse (6). En blok, der oprindeligt blev læst fra HDFS, er udfyldt i enkeltadgangsområdet. Konsekutive adgange fremmer denne blokering i multi-adgangsområdet. Området i hukommelsen er reserveret til blokke indlæst fra kolonnefamilier, der er markeret som IN_MEMORY . Uanset område bliver gamle blokke smidt ud for at give plads til nye blokke ved hjælp af en mindst nyligt brugt algoritme, deraf "Lru" i "LruBlockCache".

SlabCache

Denne implementering tildeler hukommelsesområder uden for JVM-heapen ved hjælp af DirectByteBuffer s. Disse områder indeholder brødteksten af ​​denne BlockCache . Det præcise område, hvor en bestemt blok vil blive placeret, er baseret på størrelsen af ​​blokken. Som standard tildeles to områder, der forbruger henholdsvis 80 % og 20 % af den samlede konfigurerede off-heap-cachestørrelse. Førstnævnte bruges til at cache blokke, der omtrent har målblokstørrelsen (7). Sidstnævnte rummer blokke, der er ca. 2x målblokstørrelsen. En blok placeres i det mindste område, hvor den kan passe. Hvis cachen støder på en blok, der er større end den, der kan passe i begge områder, vil den blok ikke blive cachelagret. Ligesom LruBlockCache , blokudsættelse styres ved hjælp af en LRU-algoritme.

BucketCache

Denne implementering kan konfigureres til at fungere i en af ​​tre forskellige tilstande: heapoffheap , og fil . Uanset driftstilstand, BucketCache administrerer hukommelsesområder kaldet "buckets" til at holde cachelagrede blokke. Hver spand oprettes med en målblokstørrelse. dyngen implementering skaber disse buckets på JVM-bunken; offheap implementering bruger DirectByteByffers at håndtere spande uden for JVM-bunken; fil tilstand forventer en sti til en fil på det filsystem, hvor buckets er oprettet. fil tilstanden er beregnet til brug med et backinglager med lav latens - et filsystem i hukommelsen, eller måske en fil, der sidder på SSD-lager (8). Uanset tilstand, BucketCache skaber 14 spande i forskellige størrelser. Det bruger hyppigheden af ​​blokadgang til at informere om brug, ligesom LruBlockCache , og har den samme enkeltadgang, multiadgang og hukommelsesopdeling på 25 %, 50 %, 25 %. Ligesom standardcachen styres blokudsættelse ved hjælp af en LRU-algoritme.

Caching på flere niveauer

Både SlabCache og BucketCache er designet til at blive brugt som en del af en multi-level caching-strategi. Således en del af den samlede BlockCache størrelse er tildelt en LruBlockCache eksempel. Denne instans fungerer som cache på første niveau, "L1", mens den anden cacheinstans behandles som cache på andet niveau, "L2". Men interaktionen mellem LruBlockCache og SlabCache er forskellig fra hvordan LruBlockCache og BucketCache interagere.

 SlabCache strategi, kaldet DoubleBlockCache , er altid at cache blokke i både L1 og L2 cachen. De to cache-niveauer fungerer uafhængigt:begge kontrolleres, når en blok hentes, og hver forlader blokke uden hensyn til den anden. BucketCache strategi, kaldet CombinedBlockCache , bruger L1-cachen udelukkende til Bloom- og Index-blokke. Datablokke sendes direkte til L2-cachen. I tilfælde af udsættelse af L1-blok, i stedet for at blive kasseret helt, degraderes denne blok til L2-cachen.

Hvilken skal man vælge?

Der er to grunde til at overveje at aktivere en af ​​de alternative BlockCache implementeringer. Den første er simpelthen mængden af ​​RAM, du kan dedikere til regionsserveren. Fællesskabets visdom anerkender, at den øvre grænse for JVM-heapen, hvad angår regionsserveren, er et sted mellem 14 GB og 31 GB (9). Den præcise grænse afhænger normalt af en kombination af hardwareprofil, klyngekonfiguration, formen på datatabeller og applikationsadgangsmønstre. Du ved, at du er kommet ind i farezonen, når GC holder pause og RegionTooBusyException s begynde at oversvømme dine logfiler.

Den anden tid til at overveje en alternativ cache er, når svarforsinkelse virkelig betyder noget. Ved at holde bunken nede omkring 8-12 GB kan CMS-opsamleren køre meget jævnt (10), hvilket har en målbar indflydelse på den 99. percentil af responstider. I betragtning af denne begrænsning er de eneste valg at udforske en alternativ skraldeopsamler eller tage en af ​​disse off-heap-implementeringer på tur.

Denne anden mulighed er præcis, hvad jeg har gjort. I mit næste indlæg vil jeg dele nogle uvidenskabelige, men informative eksperimentresultater, hvor jeg sammenligner svartiderne for forskellige BlockCache implementeringer.

Som altid, følg med og fortsæt med HBase!

1: MemStore akkumulerer dataredigeringer, efterhånden som de modtages, og buffer dem i hukommelsen. Dette tjener to formål:det øger den samlede mængde data, der skrives til disken i en enkelt handling, og det bevarer de seneste ændringer i hukommelsen til efterfølgende adgang i form af læsninger med lav latens. Førstnævnte er vigtig, da den holder HBase-skrivestykker nogenlunde synkroniseret med HDFS-blokstørrelser, og tilpasser HBase-adgangsmønstre med underliggende HDFS-lager. Sidstnævnte er selvforklarende og letter læseanmodninger til nyligt skrevne data. Det er værd at påpege, at denne struktur ikke er involveret i dataholdbarhed. Redigeringer skrives også til den bestilte skrivelog, HLog , som involverer en HDFS-tilføjelse med et konfigurerbart interval, normalt øjeblikkeligt.

2:Genlæsning af data fra det lokale filsystem er det bedste scenario. HDFS er trods alt et distribueret filsystem, så det værste tilfælde kræver at læse den blok over netværket. HBase gør sit bedste for at bevare datalokaliteten. Disse to artikler giver et dybdegående kig på, hvad datalokalitet betyder for HBase, og hvordan det administreres.

3:Filsystem-, HDFS- og HBase-blokke er alle forskellige, men relaterede. Det moderne I/O-undersystem er mange lag af abstraktion oven på abstraktion. Kernen i denne abstraktion er begrebet en enkelt dataenhed, kaldet en "blok". Derfor definerer alle tre af disse lagerlag deres egen blok, hver af deres egen størrelse. Generelt betyder en større blokstørrelse øget sekventiel adgangsgennemstrømning. En mindre blokstørrelse letter hurtigere tilfældig adgang.

4:Placering af BLOCKSIZE kontrol efter data er skrevet har to forgreninger. En enkelt celle er den mindste enhed af data skrevet til en DATA blok. Det betyder også en celle kan ikke spænde over flere blokke.

5:Dette er forskelligt fra MemStore , for hvilken der er en separat forekomst for hver region, der hostes af regionsserveren.

6:Indtil for ganske nylig var disse hukommelsespartitioner statisk defineret; der var ingen måde at tilsidesætte 25/50/25 opdelingen. Et givet segment, f.eks. multiadgangsområdet, kunne vokse sig større end dets 50 % tildeling, så længe de andre områder var underudnyttede. Øget udnyttelse i de andre områder vil fjerne indgange fra multi-adgangsområdet, indtil balancen 25/50/25 er opnået. Operatøren kunne ikke ændre disse standardstørrelser. HBASE-10263, forsendelse i HBase 0.98.0, introducerer konfigurationsparametre for disse størrelser. Den fleksible adfærd bibeholdes.

7:Forretningen "omtrent" er at tillade lidt slingreplads i blokstørrelser. HBase-blokstørrelsen er et groft mål eller hint, ikke en strengt håndhævet begrænsning. Den nøjagtige størrelse af en bestemt datablok vil afhænge af målblokstørrelsen og størrelsen på cellen værdier indeholdt deri. Tip til blokstørrelse er angivet som standard blokstørrelse på 64 kb.

8:Brug af BucketCachefil tilstand med en vedvarende backing-butik har en anden fordel:vedholdenhed. Ved opstart vil den lede efter eksisterende data i cachen og bekræfte deres gyldighed.

9:Som jeg forstår det, er der to komponenter, der rådgiver den øvre grænse for dette område. Først er en grænse for JVM-objektadresserbarhed. JVM er i stand til at referere til et objekt på heapen med en 32-bit relativ adresse i stedet for den fulde 64-bit native adresse. Denne optimering er kun mulig, hvis den samlede heapstørrelse er mindre end 32 GB. Se Komprimeret Ups for flere detaljer. Det andet er skraldeopsamlerens evne til at holde trit med mængden af ​​genstandsaffald i systemet. Efter hvad jeg kan se, er de tre kilder til objektafgang MemStoreBlockCache , og netværksdrift. Den første afbødes af MemSlab funktion, aktiveret som standard. Den anden er påvirket af størrelsen af ​​dit datasæt i forhold til størrelsen af ​​cachen. Den tredje kan ikke hjælpes, så længe HBase gør brug af en netværksstak, der er afhængig af datakopiering.

10:Ligesom med 8, forudsætter dette "moderne hardware". Interaktionerne her er ret komplekse og langt uden for rammerne af et enkelt blogindlæg.


  1. MongoDB:find værdi i Array med flere kriterier

  2. Node.js + MongoDB:indsæt en og returner det nyligt indsatte dokument

  3. Spring boot starter data hvile, @Notnull begrænsning virker ikke

  4. Fjern en post fra array ved hjælp af MongoDB-Java-driver