Apache HBase is de Hadoop-database en is gebaseerd op het Hadoop Distributed File System (HDFS ). HBase maakt het mogelijk om willekeurig toegang te krijgen tot gegevens die zijn opgeslagen in HDFS en deze bij te werken, maar bestanden in HDFS kunnen alleen worden toegevoegd aan en zijn onveranderlijk nadat ze zijn gemaakt. Dus je kunt je afvragen, hoe biedt HBase lees- en schrijfbewerkingen met lage latentie? In deze blogpost leggen we dit uit door het schrijfpad van HBase te beschrijven - hoe gegevens worden bijgewerkt in HBase.
Het schrijfpad is hoe een HBase put- of delete-bewerkingen voltooit. Dit pad begint bij een client, gaat naar een regioserver en eindigt wanneer gegevens uiteindelijk worden geschreven naar een HBase-gegevensbestand dat een HFile wordt genoemd . In het ontwerp van het schrijfpad zijn functies opgenomen die HBase gebruikt om gegevensverlies te voorkomen in het geval van een storing in de regioserver. Daarom kan inzicht in het schrijfpad inzicht verschaffen in het native mechanisme voor preventie van gegevensverlies van HBase.
Elke HBase-tabel wordt gehost en beheerd door sets servers die in drie categorieën vallen:
- Eén actieve hoofdserver
- Een of meer back-upmasterservers
- Veel regioservers
Regioservers dragen bij aan het afhandelen van de HBase-tabellen. Omdat HBase-tabellen groot kunnen zijn, zijn ze opgedeeld in partities die regio's worden genoemd. Elke regioserver verwerkt een of meer van deze regio's. Houd er rekening mee dat, omdat regioservers de enige servers zijn die HBase-tabelgegevens aanbieden, een hoofdservercrash geen gegevensverlies kan veroorzaken.
HBase-gegevens zijn op dezelfde manier georganiseerd als een gesorteerde kaart, waarbij de gesorteerde sleutelruimte is opgedeeld in verschillende shards of regio's. Een HBase-client werkt een tabel bij door put- of delete-opdrachten aan te roepen. Wanneer een client een wijziging aanvraagt, wordt dat verzoek standaard direct doorgestuurd naar een regioserver. Programmatisch kan een client echter de wijzigingen aan de clientzijde in de cache opslaan en deze wijzigingen in een batch naar regioservers doorspoelen door de autoflush uit te schakelen. Als autoflush is uitgeschakeld, worden de wijzigingen in de cache opgeslagen totdat flush-commits wordt aangeroepen, of de buffer vol is, afhankelijk van de buffergrootte die programmatisch is ingesteld of is geconfigureerd met parameter "hbase.client.write.buffer".
Omdat de rijsleutel is gesorteerd, is het gemakkelijk om te bepalen welke regioserver welke sleutel beheert. Een wijzigingsverzoek is voor een specifieke rij. Elke rijsleutel behoort tot een specifieke regio die wordt bediend door een regioserver. Dus op basis van de put- of delete-sleutel kan een HBase-client een goede regioserver vinden. In eerste instantie lokaliseert het het adres van de regioserver die de -ROOT- regio host vanuit het ZooKeeper-quorum. Van de root-regioserver vindt de client de locatie van de regioserver die de -META-regio host. Van de metaregioserver vinden we uiteindelijk de daadwerkelijke regioserver die de gevraagde regio bedient. Dit is een proces in drie stappen, dus de regiolocatie wordt in de cache opgeslagen om deze dure reeks bewerkingen te voorkomen. Als de cachelocatie ongeldig is (we krijgen bijvoorbeeld een onbekende regio-uitzondering), is het tijd om de regio opnieuw te lokaliseren en de cache bij te werken.
Nadat het verzoek is ontvangen door de juiste regioserver, kan de wijziging niet onmiddellijk naar een HFile worden geschreven omdat de gegevens in een HFile moeten worden gesorteerd op de rijsleutel. Hierdoor kan efficiënt naar willekeurige rijen worden gezocht bij het lezen van de gegevens. Gegevens kunnen niet willekeurig in de HFile worden ingevoegd. In plaats daarvan moet de wijziging naar een nieuw bestand worden geschreven. Als elke update naar een bestand zou worden geschreven, zouden er veel kleine bestanden worden gemaakt. Een dergelijke oplossing zou niet schaalbaar of efficiënt zijn om later samen te voegen of uit te lezen. Daarom worden wijzigingen niet onmiddellijk naar een nieuwe HFile geschreven.
In plaats daarvan wordt elke wijziging opgeslagen op een plaats in het geheugen genaamd de memstore , die goedkoop en efficiënt willekeurige schrijfbewerkingen ondersteunt. Gegevens in de memstore worden op dezelfde manier gesorteerd als gegevens in een HFile. Wanneer de memstore voldoende gegevens verzamelt, wordt de hele gesorteerde set naar een nieuwe HFile in HDFS geschreven. Het voltooien van één grote schrijftaak is efficiënt en maakt gebruik van de sterke punten van HDFS.
Hoewel het schrijven van gegevens naar de memstore efficiënt is, brengt het ook een risico-element met zich mee:informatie die in memstore is opgeslagen, wordt opgeslagen in vluchtig geheugen, dus als het systeem faalt, gaat alle memstore-informatie verloren. Om dit risico te helpen beperken, slaat HBase updates op in een write-ahead-log (WAL ) voordat u de informatie naar memstore schrijft. Op deze manier kan, als een regioserver uitvalt, informatie die is opgeslagen in de memstore van die server worden hersteld van zijn WAL.
Opmerking:WAL is standaard ingeschakeld, maar het proces van het schrijven van het WAL-bestand naar schijf verbruikt wel wat bronnen. WAL is mogelijk uitgeschakeld, maar dit mag alleen worden gedaan als het risico op gegevensverlies geen probleem is. Als u ervoor kiest WAL uit te schakelen, overweeg dan om uw eigen noodhersteloplossing te implementeren of wees voorbereid op de mogelijkheid van gegevensverlies.
De gegevens in een WAL-bestand zijn anders georganiseerd dan in HFile. WAL-bestanden bevatten een lijst met bewerkingen, waarbij één bewerking een enkele zet of verwijder vertegenwoordigt. De bewerking bevat informatie over de wijziging en de regio waarop de wijziging van toepassing is. Bewerkingen worden chronologisch geschreven, dus voor persistentie worden toevoegingen toegevoegd aan het einde van het WAL-bestand dat op schijf is opgeslagen. Omdat WAL-bestanden chronologisch zijn geordend, is het nooit nodig om naar een willekeurige plaats in het bestand te schrijven.
Naarmate WAL's groeien, worden ze uiteindelijk gesloten en wordt er een nieuw, actief WAL-bestand gemaakt om aanvullende bewerkingen te accepteren. Dit wordt het "rollen" van het WAL-bestand genoemd. Zodra een WAL-bestand is gerold, worden er geen aanvullende wijzigingen aangebracht in het oude bestand.
Standaard wordt het WAL-bestand gerold wanneer de grootte ongeveer 95% van de HDFS-blokgrootte is. U kunt de multiplier configureren met parameter:"hbase.regionserver.logroll.multiplier" en de blokgrootte met parameter:"hbase.regionserver.hlog.blocksize". Het WAL-bestand wordt ook periodiek gerold op basis van het geconfigureerde interval "hbase.regionserver.logroll.period", standaard een uur, zelfs de WAL-bestandsgrootte is kleiner dan de geconfigureerde limiet.
Door de WAL-bestandsgrootte te beperken, kunnen bestanden efficiënt opnieuw worden afgespeeld als herstel vereist is. Dit is vooral belangrijk tijdens het opnieuw afspelen van het WAL-bestand van een regio, omdat terwijl een bestand wordt afgespeeld, de bijbehorende regio niet beschikbaar is. De bedoeling is om uiteindelijk alle wijzigingen van elk WAL-bestand naar schijf te schrijven en die inhoud in een HFile te bewaren. Nadat dit is gedaan, kan het WAL-bestand worden gearchiveerd en wordt het uiteindelijk verwijderd door de LogCleaner-daemonthread. Houd er rekening mee dat WAL-bestanden dienen als een beschermende maatregel. WAL-bestanden hoeven alleen opnieuw te worden afgespeeld om updates te herstellen die anders verloren zouden gaan na een crash van een regioserver.
Een regioserver bedient veel regio's, maar heeft niet voor elke regio een WAL-bestand. In plaats daarvan wordt één actief WAL-bestand gedeeld tussen alle regio's die door de regioserver worden bediend. Omdat WAL-bestanden periodiek worden uitgerold, kan een regioserver veel WAL-bestanden hebben. Houd er rekening mee dat er slechts één actieve WAL per regioserver tegelijk is.
Uitgaande van de standaard HBase-root van "/hbase", worden alle WAL-bestanden voor een regioserverinstantie opgeslagen in dezelfde hoofdmap, die als volgt is:
/hbase/.logs/<host>, <port>,<startcode>
Bijvoorbeeld:
/hbase/.logs/srv.example.com,60020,1254173957298
WAL-logbestanden hebben de volgende naam:
/hbase/.logs/<host>, <port>,<startcode>/<host>%2C <port>%2C<startcode>.<timestamp>
Bijvoorbeeld:
/hbase/.logs/srv.example.com,60020,1254173957298/srv.example.com%2C60020%2C1254173957298.1254173957495
Elke bewerking in het WAL-bestand heeft een unieke reeks-ID. Deze id wordt verhoogd om de volgorde van bewerkingen te behouden. Telkens wanneer een logbestand wordt gerold, worden de volgende reeks-ID en de oude bestandsnaam in een in-memory map geplaatst. Deze informatie wordt gebruikt om de maximale reeks-ID van elk WAL-bestand bij te houden, zodat we gemakkelijk kunnen achterhalen of een bestand op een later tijdstip kan worden gearchiveerd wanneer een geheugenopslag naar schijf wordt leeggemaakt.
Bewerkingen en hun sequentie-ID's zijn uniek binnen een regio. Telkens wanneer een bewerking wordt toegevoegd aan het WAL-logboek, wordt de volgorde-ID van de bewerking ook geregistreerd als de laatste volgorde-ID die is geschreven. Wanneer de geheugenopslag naar de schijf wordt leeggemaakt, wordt de laatste reeks-ID die voor deze regio is geschreven, gewist. Als het laatste sequentie-ID dat naar schijf is geschreven hetzelfde is als het maximale sequentie-ID van een WAL-bestand, kan worden geconcludeerd dat alle bewerkingen in een WAL-bestand voor deze regio naar schijf zijn geschreven. Als alle bewerkingen voor alle regio's in een WAL-bestand naar schijf zijn geschreven, is het duidelijk dat splitsen of opnieuw afspelen niet nodig is en kan het WAL-bestand worden gearchiveerd.
WAL file rolling en memstore flush zijn twee afzonderlijke acties en hoeven niet samen te gebeuren. We willen echter niet te veel WAL-bestanden per regioserver bewaren om tijdrovend herstel te voorkomen in het geval dat een regioserver uitvalt. Daarom controleert HBase wanneer een WAL-bestand wordt gerolled of er te veel WAL-bestanden zijn en beslist welke regio's moeten worden leeggemaakt zodat sommige WAL-bestanden kunnen worden gearchiveerd.
In dit bericht leggen we het HBase-schrijfpad uit, dat is hoe gegevens in HBase worden gemaakt en/of bijgewerkt. Enkele belangrijke onderdelen ervan zijn:
- Hoe een client een regioserver lokaliseert,
- Memstore die snel willekeurig schrijven ondersteunt,
- WAL-bestanden als de manier om gegevensverlies te voorkomen in het geval van serverstoringen in de regio.
We zullen het hebben over HFile-indelingen, het splitsen van WAL-bestanden enzovoort in volgende berichten.