sql >> Database >  >> NoSQL >> HBase

HBase BlockCache 101

Deze blogpost is gepubliceerd op Hortonworks.com vóór de fusie met Cloudera. Sommige links, bronnen of verwijzingen zijn mogelijk niet langer nauwkeurig.

Dit blogbericht verscheen oorspronkelijk hier en wordt hier in zijn geheel gereproduceerd.

HBase is een gedistribueerde database die is gebouwd rond de kernconcepten van een geordend schrijflogboek en een log-gestructureerde samenvoegboom. Zoals bij elke database is geoptimaliseerde I/O van cruciaal belang voor HBase. Waar mogelijk is de prioriteit om helemaal geen I/O uit te voeren. Dit betekent dat geheugengebruik en cachingstructuren van het grootste belang zijn. Hiervoor hanteert HBase twee cachestructuren:de “memory store” en de “block cache”. Geheugenopslag, geïmplementeerd als de MemStore , verzamelt gegevensbewerkingen zodra ze worden ontvangen, en slaat ze op in het geheugen (1). De blokcache, een implementatie van de BlockCache interface, houdt datablokken in het geheugen nadat ze zijn gelezen.

De MemStore is belangrijk voor toegang tot recente bewerkingen. Zonder de MemStore , om toegang te krijgen tot die gegevens zoals ze in het schrijflogboek zijn geschreven, moeten de vermeldingen uit dat bestand worden gelezen en gedeserialiseerd, op zijn minst een O(n) operatie. In plaats daarvan, MemStore handhaaft een skiplist-structuur, die een O(log n) . heeft toegangskosten en vereist geen schijf-I/O. De MemStore bevat echter slechts een klein stukje van de gegevens die zijn opgeslagen in HBase.

Onderhoud leest uit de BlockCache is het primaire mechanisme waarmee HBase willekeurige leesbewerkingen kan uitvoeren met een latentie van milliseconden. Wanneer een gegevensblok uit HDFS wordt gelezen, wordt het in de cache opgeslagen in de BlockCache . Daaropvolgende uitlezingen van aangrenzende gegevens - gegevens van hetzelfde blok - hebben niet de I/O-boete van het opnieuw ophalen van die gegevens van schijf (2). Het is de BlockCache dat zal de resterende focus van dit bericht zijn.

Blocks om te cachen

Voordat u de BlockCache . begrijpt , helpt het om te begrijpen wat een HBase-"blok" precies is. In de HBase-context is een blok een enkele I/O-eenheid. Bij het wegschrijven van gegevens naar een HFile is het blok de kleinste eenheid van geschreven gegevens. Evenzo is een enkel blok de kleinste hoeveelheid gegevens die HBase kan teruglezen uit een HFile. Zorg ervoor dat u een HBase-blok niet verwart met een HDFS-blok of met de blokken van het onderliggende bestandssysteem - deze zijn allemaal verschillend (3).

HBase-blokken zijn er in 4 varianten: DATAMETAINDEX , en BLOOM .

DATA blokken slaan gebruikersgegevens op. Wanneer de BLOCKSIZE is opgegeven voor een kolomfamilie, is het een hint voor dit soort blokkering. Let wel, het is maar een hint. Tijdens het doorspoelen van de MemStore , zal HBase haar best doen om deze richtlijn te respecteren. Na elke Cell is geschreven, controleert de schrijver of het geschreven bedrag>=het doel is BLOCKSIZE . Als dat zo is, wordt het huidige blok gesloten en wordt het volgende gestart (4).

INDEX en BLOOM blokken dienen hetzelfde doel; beide worden gebruikt om het leespad te versnellen. INDEX blokken bieden een index over de Cell s in de DATA blokken. BLOOM blokken bevatten een bloeifilter over dezelfde gegevens. Dankzij de index kan de lezer snel weten waar een Cell moet worden opgeslagen. Het filter vertelt de lezer wanneer een Cell is absoluut afwezig in de gegevens.

Tot slot, META blokken slaan informatie op over de HFile zelf en andere diverse informatie - metadata, zoals je zou verwachten. Een uitgebreider overzicht van de HFile-indelingen en de rollen van verschillende bloktypen wordt gegeven in Apache HBase I/O – HFile.

HBase BlockCache en zijn implementaties

Er is een enkele BlockCache instantie in een regioserver, wat betekent dat alle gegevens van alle regio's die door die server worden gehost, dezelfde cachepool delen (5). De BlockCache wordt geïnstantieerd bij het opstarten van de regioserver en wordt gedurende de hele levensduur van het proces bewaard. Traditioneel leverde HBase slechts één BlockCache implementatie:de LruBlockCache . De 0.92-release introduceerde het eerste alternatief in HBASE-4027:de SlabCache . HBase 0.96 introduceerde een andere optie via HBASE-7404, genaamd de BucketCache .

Het belangrijkste verschil tussen de beproefde LruBlockCache en deze alternatieven is de manier waarop ze geheugen beheren. In het bijzonder, LruBlockCache is een datastructuur die volledig op de JVM-heap staat, terwijl de andere twee kunnen profiteren van geheugen van buiten de JVM-heap. Dit is een belangrijk onderscheid omdat JVM-heapgeheugen wordt beheerd door de JVM Garbage Collector, terwijl de andere dat niet zijn. In het geval van SlabCache en BucketCache , het idee is om de GC-druk die wordt ervaren door het regioserverproces te verminderen door het aantal objecten dat op de heap wordt bewaard te verminderen.

LruBlockCache

Dit is de standaardimplementatie. Met deze implementatie worden gegevensblokken in de JVM-heap opgeslagen. Het is onderverdeeld in drie gebieden:single-access, multi-access en in-memory. De gebieden hebben een grootte van 25%, 50%, 25% van het totaal BlockCache maat, respectievelijk (6). Een blok dat in eerste instantie uit HDFS wordt gelezen, wordt ingevuld in het gebied met enkele toegang. Opeenvolgende toegangen bevorderen dat blok in het gebied met meerdere toegangen. Het geheugengebied is gereserveerd voor blokken die worden geladen uit kolomfamilies die zijn gemarkeerd als IN_MEMORY . Ongeacht het gebied worden oude blokken verwijderd om plaats te maken voor nieuwe blokken met behulp van een minst recent gebruikt algoritme, vandaar de "Lru" in "LruBlockCache".

SlabCache

Deze implementatie wijst geheugengebieden buiten de JVM-heap toe met behulp van DirectByteBuffer s. Deze gebieden vormen de hoofdtekst van deze BlockCache . Het precieze gebied waarin een bepaald blok wordt geplaatst, is gebaseerd op de grootte van het blok. Standaard worden twee gebieden toegewezen, die respectievelijk 80% en 20% van de totale geconfigureerde off-heap cachegrootte in beslag nemen. De eerste wordt gebruikt om blokken te cachen die ongeveer de doelblokgrootte hebben (7). De laatste bevat blokken die ongeveer 2x de doelblokgrootte zijn. Een blok wordt in het kleinste gebied geplaatst waar het past. Als de cache een blok tegenkomt dat groter is dan in beide gebieden past, wordt dat blok niet in de cache opgeslagen. Zoals LruBlockCache , blokkering wordt beheerd met behulp van een LRU-algoritme.

BucketCache

Deze implementatie kan worden geconfigureerd om in een van de drie verschillende modi te werken: heapoffheap , en file . Ongeacht de bedieningsmodus, de BucketCache beheert geheugengebieden die "buckets" worden genoemd voor het bewaren van blokken in de cache. Elke bucket wordt gemaakt met een doelblokgrootte. De heap implementatie creëert die buckets op de JVM-heap; offheap implementatie maakt gebruik van DirectByteByffers om buckets buiten de JVM-heap te beheren; file mode verwacht een pad naar een bestand op het bestandssysteem waarin de buckets worden gemaakt. file modus is bedoeld voor gebruik met een backing-store met lage latentie - een in-memory bestandssysteem, of misschien een bestand op SSD-opslag (8). Ongeacht de modus, BucketCache creëert 14 emmers van verschillende groottes. Het gebruikt de frequentie van blokkeringstoegang om het gebruik te informeren, net als LruBlockCache , en heeft dezelfde uitsplitsing met één toegang, meerdere toegang en in-memory van 25%, 50%, 25%. Net als de standaardcache, wordt blokkering beheerd met behulp van een LRU-algoritme.

Caching op meerdere niveaus

Zowel de SlabCache en BucketCache zijn ontworpen om te worden gebruikt als onderdeel van een cachingstrategie op meerdere niveaus. Dus een deel van de totale BlockCache grootte wordt toegewezen aan een LruBlockCache voorbeeld. Deze instantie fungeert als de cache van het eerste niveau, "L1", terwijl de andere cache-instantie wordt behandeld als de cache van het tweede niveau, "L2". De interactie tussen LruBlockCache en SlabCache verschilt van hoe de LruBlockCache en de BucketCache interactie.

De SlabCache strategie, genaamd DoubleBlockCache , is om altijd blokken in zowel de L1- als de L2-cache te cachen. De twee cacheniveaus werken onafhankelijk:beide worden gecontroleerd bij het ophalen van een blok en elk verwijdert blokken zonder rekening te houden met de ander. De BucketCache strategie, genaamd CombinedBlockCache , gebruikt de L1-cache exclusief voor Bloom- en Index-blokken. Datablokken worden rechtstreeks naar de L2-cache gestuurd. In het geval van verwijdering van een L1-blok, wordt dat blok niet volledig verwijderd, maar gedegradeerd naar de L2-cache.

Welke te kiezen?

Er zijn twee redenen om een ​​van de alternatieve BlockCache in te schakelen implementaties. De eerste is gewoon de hoeveelheid RAM die u aan de regioserver kunt besteden. Community-wijsheid erkent dat de bovengrens van de JVM-heap, voor zover het de regioserver betreft, ergens tussen 14 GB en 31 GB ligt (9). De precieze limiet hangt meestal af van een combinatie van hardwareprofiel, clusterconfiguratie, de vorm van gegevenstabellen en toegangspatronen voor toepassingen. U weet dat u zich in de gevarenzone bevindt wanneer GC pauzeert en RegionTooBusyException s beginnen je logs te overspoelen.

Het andere moment om een ​​alternatieve cache te overwegen, is wanneer de responslatentie echt zaken. Door de heap rond de 8-12 GB te houden, kan de CMS-collector zeer soepel werken (10), wat een meetbare impact heeft op het 99e percentiel van responstijden. Gezien deze beperking, zijn de enige keuzes om een ​​alternatieve vuilnisman te verkennen of een van deze off-heap-implementaties uit te proberen.

Deze tweede optie is precies wat ik heb gedaan. In mijn volgende bericht zal ik enkele onwetenschappelijke, maar informatieve experimentresultaten delen, waarbij ik de reactietijden voor verschillende BlockCache vergelijk. implementaties.

Blijf zoals altijd op de hoogte en ga door met de HBase!

1:De MemStore verzamelt gegevensbewerkingen zodra ze worden ontvangen en buffert ze in het geheugen. Dit heeft twee doelen:het verhoogt de totale hoeveelheid gegevens die in één keer naar de schijf wordt geschreven, en het bewaart die recente wijzigingen in het geheugen voor latere toegang in de vorm van leesbewerkingen met lage latentie. De eerste is belangrijk omdat het de HBase-schrijfblokken ongeveer synchroon houdt met de HDFS-blokgroottes, waardoor HBase-toegangspatronen worden uitgelijnd met de onderliggende HDFS-opslag. Dit laatste spreekt voor zich en vergemakkelijkt het lezen van recent geschreven gegevens. Het is de moeite waard erop te wijzen dat deze structuur niet betrokken is bij de duurzaamheid van gegevens. Bewerkingen worden ook weggeschreven naar het bestelde schrijflogboek, de HLog , wat een HDFS-toevoegbewerking inhoudt met een configureerbaar interval, meestal onmiddellijk.

2:Het opnieuw lezen van gegevens uit het lokale bestandssysteem is het beste scenario. HDFS is tenslotte een gedistribueerd bestandssysteem, dus in het ergste geval moet dat blok via het netwerk worden gelezen. HBase doet zijn best om de gegevenslocatie te behouden. Deze twee artikelen bieden een diepgaand inzicht in wat gegevenslocatie betekent voor HBase en hoe het wordt beheerd.

3:Bestandssysteem-, HDFS- en HBase-blokken zijn allemaal verschillend, maar gerelateerd. Het moderne I/O-subsysteem bestaat uit vele lagen van abstractie op abstractie. De kern van die abstractie is het concept van een enkele gegevenseenheid, een 'blok' genoemd. Daarom definiëren alle drie deze opslaglagen hun eigen blok, elk van hun eigen grootte. In het algemeen betekent een grotere blokgrootte een grotere sequentiële toegangsdoorvoer. Een kleinere blokgrootte maakt snellere willekeurige toegang mogelijk.

4:Het plaatsen van de BLOCKSIZE check nadat gegevens zijn geschreven heeft twee vertakkingen. Een enkele Cell is de kleinste gegevenseenheid die naar een DATA . wordt geschreven blok. Het betekent ook een Cell kan niet meerdere blokken overspannen.

5:Dit is anders dan de MemStore , waarvoor er een aparte instantie is voor elke regio die wordt gehost door de regioserver.

6:Tot voor kort waren deze geheugenpartities statisch gedefinieerd; er was geen manier om de splitsing van 25/50/25 teniet te doen. Een bepaald segment, bijvoorbeeld het gebied met meerdere toegangen, zou groter kunnen worden dan de toegewezen 50%, zolang de andere gebieden onderbenut waren. Verhoogd gebruik in de andere gebieden zal toegang tot het gebied met meerdere toegangen verwijderen totdat het 25/50/25-saldo is bereikt. De operator kan deze standaardformaten niet wijzigen. HBASE-10263, geleverd in HBase 0.98.0, introduceert configuratieparameters voor deze formaten. Het flexibele gedrag blijft behouden.

7:Het "ongeveer" bedrijf is om wat speelruimte in blokgroottes toe te staan. HBase-blokgrootte is een ruw doel of hint, geen strikt afgedwongen beperking. De exacte grootte van een bepaald gegevensblok hangt af van de grootte van het doelblok en de grootte van de Cell daarin opgenomen waarden. De hint voor blokgrootte is gespecificeerd als de standaard blokgrootte van 64 kb.

8:De BucketCache . gebruiken in file modus met een persistent backing-archief heeft nog een ander voordeel:persistentie. Bij het opstarten zoekt het naar bestaande gegevens in de cache en controleert het de geldigheid ervan.

9:Zoals ik het begrijp, zijn er twee componenten die de bovengrens van dit bereik adviseren. Ten eerste is er een limiet op de adresseerbaarheid van JVM-objecten. De JVM kan verwijzen naar een object op de heap met een 32-bits relatief adres in plaats van het volledige 64-bits native adres. Deze optimalisatie is alleen mogelijk als de totale heapgrootte kleiner is dan 32 GB. Zie Gecomprimeerde Oeps voor meer informatie. De tweede is het vermogen van de vuilnisman om de hoeveelheid objectverloop in het systeem bij te houden. Voor zover ik kan zien, zijn de drie bronnen van objectverloop MemStoreBlockCache en netwerkactiviteiten. De eerste wordt verzacht door de MemSlab functie, standaard ingeschakeld. De tweede wordt beïnvloed door de grootte van uw dataset versus de grootte van de cache. De derde kan niet worden geholpen zolang HBase gebruik maakt van een netwerkstack die afhankelijk is van gegevenskopie.

10:Net als bij 8, wordt hier uitgegaan van "moderne hardware". De interacties hier zijn behoorlijk complex en vallen ver buiten het bestek van een enkele blogpost.


  1. $nin met de $expr

  2. Meteor:verschil tussen namen voor collecties, variabelen, publicaties en abonnementen?

  3. Hoe gebruik ik mongodb met elektron?

  4. Meerdere waarden atomair uit de Redis-gegevensstructuur halen?