Pre nešto više od godinu dana postala sam Big Data softverski inženjer u Ibis Instruments Big Data timu. U proteklih nekoliko godina ovaj tim je uspeo da razvije sopstveno Big Data rešenje Ibis Performance Insights, iPi. Do sada smo razvili nekoliko iPi Big Data okruženja i kao što to obično biva u developerskom svetu, tokom svakog smo nailazili na različite izazove i prepreke.
A zašto postoje izazovi i prepreke ako ne da bismo iz njih učili? Zato sam rešila da jedno od najznačajnih iskustava koje se tiče HBase-a i njegovog uticaja na jedno od naših najvećih i najznačajnijih Big Data okruženja rešila da podelim sa drugim developerima.
Prva stvar koja je važna za razumevanje problema je arhitektura iPi-ja. Dakle…
Šta je iPi?
iPi je Big Data rešenje zasnovano na open source tehnologijama. U njegovoj osnovi nalazi se izuzetno moćna Hadoop platforma koja zajedno sa ostalim Big Data komponentama čini jedan veliki ekosistem odgovoran za skladištenje, procesuiranje i obradu velikih količina podataka u gotovo realnom vremenu.
Ova platforma distribuirana je na nekoliko mašina koje zajedno čine Big Data klaster i organizovane su u master-slave arhitekturu. Dve od ovih mašina, takozvani namenode-ovi, imaju ulogu u administraciji i skladištenju važnih informacija o sistemu, dok slave mašine, odnosno datanode-ovi, imaju ulogu u izvršavanju taskova zadatih od strane aktivnog namenode-a i odgovorni su za pouzdano i efikasno skladištenje i obradu podataka.
Drugi značajan deo iPi-ja čini web aplikacija, koja služi za prikazivanje rezultata dobijenih pomoću Big Data klastera, ali kako bi ovaj post zadržao konciznost i jasnoću, ova aplikacija biće tema nekog od narednih postova.
Pošto sam već napisala nekoliko redova o Hadoop platformi, možete pretpostaviti da smo ulogu pouzdanog skladištenja podatka prepustili upravo jednoj od osnovnih komponenti Hadoopa, a to je HDFS (Hadoop Distributed File System). HDFS je izuzetno značajna komponenta, jer može da podrži skladištenje velikih količina podataka, vrlo lako postižući fault-tolerant stanje kopirajući podatke u preciziran broj instanci. Međutim, ove značajne kvalitete HDFS plaća I/O kašnjenjem i tu na scenu stupa upravo HBase, distribuirano, skalabilno skladište podataka koje radi preko HDFS-a.
HBase predstavlja ključnu komponentu za IPI jer je dizajniran tako da podrži brz pristup tabelama koje sadrže bilione redova i milione kolona, što mu omogućava da efikasno opslužuje aplikacije koje zahtevaju brz, proizvoljan pristup velikim setovima podataka što posebno odgovara našoj web aplikaciji u okviru iPi-ja.
Dodatno, radi optimizacije brzog čitanja i upisa velike količine Time Seriespodataka, preko HBase-a instaliran je specijalizovan layer za brz upis i čitanje podataka, pri čemu instanca ovog alata postoji na svakoj mašini u Big Data klasteru. Sve ove instance gledaju u istu HBase tabelu u kojoj se nalaze podaci, pri čemu se instance na datanode-ovima koriste isključivo za upis, a instance na namenode-ovima za čitanje podataka.
Problem
Već sam napomenula da je Big Data okruženje koje je tema ovog posta jedno od naših najvećih i najznačajnih okruženja. Big Data klaster ovog okruženja sastoji se od 11 mašina kapaciteta od oko 10TB i 64GB RAM-a, pri čemu devet datanode-ova trenutno ima zadatak da pouzdano i efikasno skladišti i obradi 50TB kompresovanih podataka, a podrazumeva se da će ovaj broj nastaviti da raste.
Big Data oblast iz dana u dan postaje sve popularnija i sve više evoluira, pa ukoliko želite da ostanete relevantni na ovom polju, nedopustivo je da sebi dozvolite stagnaciju u bilo kom aspektu. Stoga smo se mi kao tim trudili da u što kraćem vremenskom roku razvijemo i implementiramo što više novih funkcionalnosti u okviru iPi-ja, što je često značilo znatno veći read i write load na klaster. Iako je zauzeće prostora na HDFS-u blizu 50 odsto, broj read i writezahteva upućenih klasteru se strmoglavo povećao i brzo je postalo očigledno da inicijalna konfiguracija sa kojom smo započeli celu iPi priču više nije odgovarala, što se veoma loše odrazilo na performanse sistema.
Mada sam u prethodnom odeljku imala samo reči hvale o HBase-u i njegovim performansama, u našem slučaju je epicentar svih problema bio upravo HBase. HBase je baziran na JAVA-i, što znači da automatski menadžment memorije, odnosno JAVA Garbage Collection, ima krucijalan uticaj na njegove performanse.
Podaci koje koristimo u okviru Big Data okruženja su u HBase-u organizovani u obliku jedne velike tabele distribuirane na datanode-ovima u okviru klastera. Ova tabela izdeljena je na delove koji se nazivaju regionima i na svakom datanode-unalazi se proces HBase RegionServer koji je zadužen za pristupanje regionima u okviru svog datanode-a prilikom read i write operacija.
Kada se broj read i write zahteva upućenih RegionServeru strmoglavo poveća, a inicijalna konfiguracija ne može da podrži ovaj load, dolazi do takozvanih „stop the world“ pauza. Tokom ovih pauza, RegionServer sve svoje resurse koristi za čišćenje heapa, što onemogućava pravilan rad sistema.
U našem slučaju, dešavalo se da ove pauze traju po nekoliko minuta u najkritičnijim situacijama, što znači da u tih nekoliko minuta RegionServer nije mogao da izvrši read ili write zahtev. Ovakve situacije imale su direktan uticaj na web aplikaciju, što je veliki problem kako za nas, tako i za korisnika. Web aplikacija je radila vrlo usporeno, a dešavalo se da su podaci prikazivani na izveštajima nerealni i besmisleni. Jasno, neophodno je bilo brzo pronaći efikasno i dugotrajno rešenje.
Za rešenje ovakvih situacija postoje tri opcije: smanjenje loada na sistem, dodavanje novih datanode-ova u klaster ili HBase tjuning. U našem slučaju, prva opcija ne predstavlja rešenje jer naš dugoročni plan ne obuhvata bilo kakav downgrade iPi-ja. Druga opcija, koja zapravo predstavlja i najjednostavnije rešenje, nije bila moguća u tako kratkom vremenskom roku. Stoga smo bili prinuđeni na promenu konfiguracije HBase-a, odnosno HBase tjuning.
Kako smo to uradili?
Prvi korak u rešavanju našeg problema bilo je detaljno razumevanje procesa koji dovodi do „stop the world“ pauza koje stopiraju aplikaciju.
Naša inicijalna konfiguracija podrazumevala je Garbage First Collector, što je sasvim razumljivo na veličinu heapa od 8GB. Ovaj Garbage Collector predstavlja jedan od najefikasnijih i najperformantnijih Garbage Collectora razvijenih za multiprocesorske mašine sa veličinama heapa iznad 6GB. Stoga nismo želeli da menjamo ovaj parametar, ali smo imali u vidu mogućnost povećanja heapa.
Takođe, naša inicijalna konfiguracija podrazumevala je željenu GC pauzu od 200ms. Prateći stanje na graficima za trajanje Garbage Collectiona, primetili smo da se ova situacija dešava u samo dva odsto vremena. Trajanje Garbage Collectiona je imalo velike oscilacije, od 150ms do 80s, a problematični Garbage Collection se izvršavao na svakih sat vremena. Grafik koji se tiče zauzeća heapaprikazivao je konstatnu vrednost zauzeća od 6GB.
Zatim smo pogledali i dve najznačajnije in-memory komponente RegionServera, MemStore i BlockCache. Najjednostavnije rečeno, MemStore prestavlja write cache koji ima ulogu storage-a za nove podatke koji još uvek nisu upisani na disk. Kada se u MemStore-u nakupi dovoljno podataka (u našem slučaju je to 128MB), podaci se spajaju u HFile-ove i upisuju na disk. Slično, BlockCache predstavlja read cache. U našem slučaju, obe ove komponente bile su konfigurisane tako da zauzimaju maksimalno po 40% heapa.
Jedna od najznačajnih informacija koja nas je usmerila ka optimalnom rešenju problema jeste bila vreme tokom kojeg je trajanje Garbage Collectiona bilo najduže. Ovo vreme podudaralo se sa upisom najveće količine podataka na klaster. Takođe, u logovima za HBase Region Server su se u to vreme mogle videti informacije o tome kako su fajlovi veličine čak 5MB bili upisani na disk, što se nije poklopalo sa našom željenom vrednošću od 128MB. Zbog toga smo dolazili u situacije zauzeća celokupnog MemStore-a. U takvim situacijama, HBase vrši nasilno upisivanje fajlova na disk (flush-ing) i blokira svaki update. U prilog ovoj tvrdnji ide i činjenica da su se na klasteru tokom najdužeg trajanja Garbage Collectiona javljala Flush Queue Size upozorenja.
Kako količina MemStore-a očigledno nije bila dovoljna za write load na naš klaster,odlučili smo da povećamo heap na 12GB. Pedeset odsto toga podredili smo MemStore-u, a 30% BlockCache-u. Na taj način veličina BlockCache-a koji je i ranije imao dovoljno prostora nije značajno promenjena, a veličina MemStore je imala značajno proširenje.
Nažalost, ne postoji univerzalna formula koja govori koliko heapa je potrebno dodeliti kom sistemu. Naš izbor veličine heapa je rezultat mnogobrojnih testiranja, ali sveopšta preporuka jeste da veličina heapa ne bi trebalo da prelazi 16GB. Naravno, mora se imati u vidu i kapacitet mašine.
Pored ovih parametara, bilo je potrebno uvesti još neke kako bi se problem u potpunosti otklonio. Željena GC pauza je ostala na istoj vrednosti od 200ms, pri čemu su prema Oracle preporuci uvedeni još neki parametri koji pomažu dostizanju ove vrednosti. Sigurna preporuka je parametar -XX:+ParallelRefProcEnabled. Primećeno je da ovaj parametar smanjuje GC pauze za čak 30%, što je bio i naš slučaj.
Finalno, parametar –XX:InitiatingHeapOccupancyPercent govori koliko treba biti minimalno zauzeće heapa pri kom treba započeti marking fazu. Ovaj parametar umnogome može pomoći performansama, ali ukoliko je pogrešno postavljen, može imati poguban uticaj po performanse, bez obzira na veličinu heapa. Defaultvrednost je 45%, iako postoji neformalno pravilo da ova vrednost treba da bude veća od veličine MemStore-a i BlockCache-a zajedno. Kako nismo želeli da rizikujemo sa značajnim okruženjem, testiranjem smo došli do zaključka da je u našem slučaju 70% ipak optimalna vrednost.
Šta smo postigli?
Nakon izmene konfiguracije HBase-a, nismo imali problema sa performansama sistema. Web aplikacija je nastavila da radi optimalno, a prethodna upozorenja se ne javljaju. Prosečna GC pauza je 215ms, sa standardnom devijacijom od 10ms, što je solidan rezultat.
Takođe, svesni smo da postoji znatno veći izbor parametara, ali tokom pronalaženja optimalne konfiguracije nosili smo se mišlju da ona bude što je jednostavnija moguća, zbog rizika koju svaka komplikovana konfiguracija sa sobom nosi.
HBase Garbage Collection je krucijalan, ali i vrlo nepredvidiv faktor koji se tiče optimalnog rada Big Data okruženja. Ukoliko JVM parametri nisu dobro podešeni može doći do katastrofalnih posledica po ceo sistem. U našem slučaju, katastrofa je izbegnuta i pronađeno je optimalno rešenje. Nažalost, ne postoji univerzalna formula kojom se dolazi do najboljeg rešenja jer svaki sistem ima drugačije zahteve i namene, ali postoje smernice koje vam značajno mogu olakšati put.