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

Tuning Java Garbage Collection til HBase

Dette gæsteindlæg fra Intels Java-performancearkitekt Eric Kaczmarek (oprindeligt udgivet her) udforsker, hvordan man tuner Java garbage collection (GC) til Apache HBase med fokus på 100 % YCSB-læsninger.

Apache HBase er et Apache open source-projekt, der tilbyder NoSQL-datalagring. Ofte brugt sammen med HDFS, er HBase meget brugt over hele verden. Kendte brugere inkluderer Facebook, Twitter, Yahoo og mere. Fra udviklerens perspektiv er HBase en "distribueret, versionsbestemt, ikke-relationel database modelleret efter Googles Bigtable, et distribueret lagringssystem til strukturerede data". HBase kan nemt håndtere meget høj gennemstrømning ved enten at skalere op (dvs. udrulning på en større server) eller udskalere (dvs. udrulning på flere servere).

Fra en brugers synspunkt betyder latensen for hver enkelt forespørgsel meget. Mens vi arbejder med brugere for at teste, tune og optimere HBase-arbejdsbelastninger, støder vi nu på et betydeligt antal, som virkelig ønsker 99. percentil-driftsforsinkelser. Det betyder en rundtur, fra klientanmodning til svaret tilbage til klienten, alt sammen inden for 100 millisekunder.

Flere faktorer bidrager til variation i latens. En af de mest ødelæggende og uforudsigelige forsinkelsesindtrængere er Java Virtual Machines (JVM's) "stop the world"-pauser til affaldsindsamling (hukommelsesoprydning).

For at løse det, prøvede vi nogle eksperimenter med Oracle jdk7u21 og jdk7u60 G1 (Garbage 1st) samler. Serversystemet vi brugte var baseret på Intel Xeon Ivy-bridge EP-processorer med Hyper-threading (40 logiske processorer). Den havde 256 GB DDR3-1600 RAM og tre 400 GB SSD'er som lokal lagring. Denne lille opsætning indeholdt en master og en slave, konfigureret på en enkelt node med belastningen passende skaleret. Vi brugte HBase version 0.98.1 og lokalt filsystem til HFile-lagring. HBase testtabel var konfigureret som 400 millioner rækker, og det var 580 GB i størrelse. Vi brugte standard HBase-heap-strategien:40% for blockcache, 40% for memstore. YCSB blev brugt til at drive 600 arbejdstråde, der sendte anmodninger til HBase-serveren.

Følgende diagrammer viser, at jdk7u21 kører 100 % læst i én time ved hjælp af -XX:+UseG1GC -Xms100g -Xmx100g -XX:MaxGCPauseMillis=100 . Vi specificerede den affaldsopsamler, der skulle bruges, bunkens størrelse og den ønskede pausetid for affaldsindsamling (GC) "stop verden".

Figur 1:Vilde udsving i GC-pausetid

I dette tilfælde fik vi vildt svingende GC-pauser. GC-pausen havde et område fra 7 millisekunder til 5 hele sekunder efter en indledende stigning, der nåede så højt som 17,5 sekunder.

Følgende diagram viser flere detaljer under steady state:

Figur 2:GC-pausedetaljer under steady state

Figur 2 fortæller os, at GC-pauserne faktisk kommer i tre forskellige grupper:(1) mellem 1 og 1,5 sekunder; (2) mellem 0,007 sekunder til 0,5 sekunder; (3) spidser mellem 1,5 sekunder til 5 sekunder. Dette var meget mærkeligt, så vi testede den senest udgivne jdk7u60 for at se, om dataene ville være anderledes:

Vi kørte de samme 100 % læsetests med nøjagtig de samme JVM-parametre:-XX:+UseG1GC -Xms100g -Xmx100g -XX:MaxGCPauseMillis=100 .

Figur 3:Stærkt forbedret håndtering af pausetidsspidser

Jdk7u60 forbedrede i høj grad G1's evne til at håndtere pausetidsspidser efter indledende spids under afviklingsstadiet. Jdk7u60 lavede 1029 unge og blandede GC'er i løbet af en times løb. GC skete omkring hvert 3,5 sekund. Jdk7u21 lavede 286 GC'er, hvor hver GC fandt sted cirka hvert 12.6 sekund. Jdk7u60 var i stand til at styre pausetiden mellem 0,302 og 1 sekund uden større spidser.

Figur 4 nedenfor giver os et nærmere kig på 150 GC-pauser under steady state:

Figur 4:Bedre, men ikke godt nok

Under steady state var jdk7u60 i stand til at holde den gennemsnitlige pausetid omkring 369 millisekunder. Det var meget bedre end jdk7u21, men det opfyldte stadig ikke vores krav på 100 millisekunder givet af –Xx:MaxGCPauseMillis=100 .

For at bestemme, hvad vi ellers kunne gøre for at få vores 100 millioner sekunders pausetid, var vi nødt til at forstå mere om adfærden af ​​JVM's hukommelseshåndtering og G1 (Garbage First) skraldeopsamler. De følgende figurer viser, hvordan G1 fungerer på Young Gen-kollektionen.

Figur 5:Slide fra 2012 JavaOne-præsentationen af ​​Charlie Hunt og Monica Beckwith:"G1 Garbage Collector Performance Tuning"

Når JVM starter, baseret på JVM-startparametrene, beder den operativsystemet om at allokere en stor kontinuerlig hukommelsesdel til at være vært for JVM'ens heap. Denne hukommelsesdel er opdelt af JVM i regioner.

Figur 6:Slide fra 2012 JavaOne-præsentationen af ​​Charlie Hunt og Monica Beckwith:"G1 Garbage Collector Performance Tuning"

Som figur 6 viser, kommer hvert objekt, som Java-programmet tildeler ved hjælp af Java API, først til Eden-rummet i den unge generation til venstre. Efter et stykke tid bliver Eden fuld, og en Young generation GC udløses. Objekter, der stadig refereres til (dvs. "levende"), kopieres til Survivor-rummet. Når objekter overlever flere GC'er i den unge generation, bliver de forfremmet til den gamle generations rum.

Når Young GC sker, stoppes Java-applikationens tråde for sikkert at markere og kopiere levende objekter. Disse stop er de berygtede "stop-the-world" GC-pauser, som gør, at applikationerne ikke reagerer, indtil pauserne er forbi.

Figur 7:Slide fra 2012 JavaOne-præsentationen af ​​Charlie Hunt og Monica Beckwith:"G1 Garbage Collector Performance Tuning"

Den gamle generation kan også blive overfyldt. På et bestemt niveau – kontrolleret af -XX:InitiatingHeapOccupancyPercent=? hvor standarden er 45 % af den samlede heap - en blandet GC udløses. Den samler både Young gen og Old gen. De blandede GC-pauser styres af, hvor lang tid det tager Young gen at rydde op, når der sker blandet GC.

Så vi kan se i G1, at "stop verden" GC-pauserne er domineret af, hvor hurtigt G1 kan markere og kopiere levende objekter ud af Eden-rummet. Med dette i tankerne vil vi analysere, hvordan HBase-hukommelsestildelingsmønsteret vil hjælpe os med at tune G1 GC for at få vores ønskede pause på 100 millisekunder.

I HBase er der to strukturer i hukommelsen, der bruger det meste af dens bunke:BlockCache , cachelagring af HBase-filblokke til læseoperationer, og Memstore cachelagre de seneste opdateringer.

Figur 8:I HBase optager to strukturer i hukommelsen det meste af dens bunke.

Standardimplementeringen af ​​HBases BlockCache er LruBlockCache , som blot bruger et stort byte-array til at være vært for alle HBase-blokkene. Når blokke "smides ud", fjernes referencen til den pågældende bloks Java-objekt, hvilket gør det muligt for GC'en at flytte hukommelsen.

Nye objekter, der danner LruBlockCache og Memstore gå først til den unge generations Eden-rum. Hvis de lever længe nok (dvs. hvis de ikke bliver smidt ud fra LruBlockCache eller skyllet ud af Memstore), så efter adskillige unge generationer af GC'er, tager de vej til den gamle generation af Java-bunken. Når den gamle generations ledige plads er mindre end en given threshOld (InitiatingHeapOccupancyPercent til at starte med), sætter blandet GC ind og rydder nogle døde genstande ud i den gamle generation, kopierer levende genstande fra den unge generation og genberegner den unge generations Eden og den gamle generations HeapOccupancyPercent . Til sidst, når HeapOccupancyPercent når et vist niveau, en FULL GC sker, hvilket gør, at kæmpe "stop verden" GC holder pause for at rydde op i alle døde genstande inde i den gamle generation.

Efter at have studeret GC-loggen produceret af "-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy “, lagde vi mærke til HeapOccupancyPercent voksede sig aldrig stor nok til at fremkalde en fuld GC under HBase 100 % aflæsning. De GC-pauser, vi så, var domineret af den unge generations "stop the world"-pauser og den stigende referencebehandling over tiden.

Efter at have gennemført denne analyse foretog vi tre grupper af ændringer i standard G1 GC-indstillingen:

  1. Brug -XX:+ParallelRefProcEnabled Når dette flag er slået til, bruger GC flere tråde til at behandle de stigende referencer under Young og mixed GC. Med dette flag for HBase reduceres GC-remarkeringstiden med 75 %, og den samlede GC-pausetid reduceres med 30 %.
  2. Set -XX:-ResizePLAB and -XX:ParallelGCThreads=8+(logical processors-8)(5/8) Promotion Local Allocation Buffers (PLAB'er) bruges under Young-indsamling. Der bruges flere tråde. Hver tråd skal muligvis tildele plads til objekter, der kopieres enten i Survivor eller Old space. PLAB'er er påkrævet for at undgå konkurrence fra tråde om delte datastrukturer, der administrerer ledig hukommelse. Hver GC-tråd har en PLAB for overlevelsesrum og en for gammelt rum. Vi vil gerne stoppe med at ændre størrelsen på PLAB'er for at undgå de store kommunikationsomkostninger mellem GC-tråde, såvel som variationer under hver GC. Vi vil gerne fastsætte antallet af GC-tråde til størrelsen beregnet af 8+ (logiske processorer-8)( 5/8). Denne formel blev for nylig anbefalet af Oracle. Med begge indstillinger er vi i stand til at se jævnere GC-pauser under løbeturen.
  3. Skift -XX:G1NewSizePercent standard fra 5 til 1 for 100 GB heapBaseret på output fra -XX:+PrintGCDetails and -XX:+PrintAdaptiveSizePolicy , lagde vi mærke til, at årsagen til, at G1 ikke nåede vores ønskede 100GC-pausetid, var den tid, det tog at behandle Eden. Med andre ord tog G1 i gennemsnit 369 millisekunder at tømme 5 GB Eden under vores test. Vi ændrede derefter Eden-størrelsen ved hjælp af -XX:G1NewSizePercent=
    flag fra 5 ned til 1. Med denne ændring så vi GC-pausetiden reduceret til 100 millisekunder.

Fra dette eksperiment fandt vi ud af, at G1's hastighed til at rense Eden er omkring 1 GB pr. 100 millisekunder, eller 10 GB pr. sekund for den HBase-opsætning, vi brugte.

Baseret på den hastighed kan vi indstille -XX:G1NewSizePercent=
så Eden-størrelsen kan holdes omkring 1 GB. For eksempel:

  • 32 GB heap, -XX:G1NewSizePercent=3
  • 64 GB heap, –XX:G1NewSizePercent=2
  • 100 GB og derover heap, -XX:G1NewSizePercent=1
  • Så vores sidste kommandolinjeindstillinger for HRegionserveren er:
    • -XX:+UseG1GC
    • -Xms100g -Xmx100g (Habestørrelse brugt i vores tests)
    • -XX:MaxGCPauseMillis=100 (Ønsket GC-pausetid i tests)
    • XX:+ParallelRefProcEnabled
    • -XX:-ResizePLAB
    • -XX:ParallelGCThreads= 8+(40-8)(5/8)=28
    • -XX:G1NewSizePercent=1

Her er GC pausetidsdiagram for at køre 100 % læseoperation i 1 time:

Figur 9:De højeste indledende bundfældningsspidser blev reduceret med mere end halvdelen.

I dette diagram blev selv de højeste indledende afsætningsspidser reduceret fra 3,792 sekunder til 1,684 sekunder. De fleste indledende spidser var mindre end 1 sekund. Efter forliget var GC i stand til at holde pausetiden omkring 100 millisekunder.

Diagrammet nedenfor sammenligner jdk7u60-kørsler med og uden tuning under steady state:

Figur 10:jdk7u60 kører med og uden tuning under steady state.

Den simple GC-tuning, vi beskrev ovenfor, giver ideelle GC-pausetider, omkring 100 millisekunder, med gennemsnitligt 106 millisekunder og 7 millisekunders standardafvigelse.

Oversigt

HBase er en responstidskritisk applikation, der kræver GC-pausetid for at være forudsigelig og overskuelig. Med Oracle jdk7u60, baseret på GC-oplysningerne rapporteret af -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy , er vi i stand til at justere GC-pausetiden ned til vores ønskede 100 millisekunder.

Eric Kaczmarek er en Java-ydeevnearkitekt i Intels Software Solution Group. Han leder indsatsen hos Intel for at aktivere og optimere Big Data-rammer (Hadoop, HBase, Spark, Cassandra) til Intel-platforme.

Software og arbejdsbelastninger, der bruges i ydeevnetest, er muligvis kun blevet optimeret til ydeevne på Intel-mikroprocessorer. Ydeevnetest, såsom SYSmark og MobileMark, måles ved hjælp af specifikke computersystemer, komponenter, software, operationer og funktioner. Enhver ændring af nogen af ​​disse faktorer kan få resultaterne til at variere. Du bør konsultere andre oplysninger og ydeevnetest for at hjælpe dig med fuldt ud at evaluere dine påtænkte køb, herunder produktets ydeevne, når det kombineres med andre produkter.

Intel-processortal er ikke et mål for ydeevne. Processornumre adskiller funktioner inden for hver processorfamilie. Ikke på tværs af forskellige processorfamilier. Gå til:http://www.intel.com/products/processor_number.

Copyright 2014 Intel Corp. Intel, Intel-logoet og Xeon er varemærker tilhørende Intel Corporation i USA og/eller andre lande.


  1. MongoDB $exp

  2. Sådan finder du længden af ​​arrayet mongodb

  3. Forebyggende sikkerhed med revisionslogning til MongoDB

  4. Import af Dato-datatype ved hjælp af mongoimport