KĂ”rge jĂ”udlus on ĂŒks peamisi nĂ”udeid suurandmete töötlemisel. Meie, andmete laadimise meeskonnas Sberis, töötleme praktiliselt kĂ”iki tehinguid meie Hadoopi Andmekeskuses, mistĂ”ttu puutume kokku tĂ”eliselt suurte informatsiooni voogudega. Loomulikult otsime pidevalt vĂ”imalusi jĂ”udluse tĂ”stmiseks ning nĂŒĂŒd tahame rÀÀkida, kuidas suudame parandada HBase RegionServer'i ja HDFS-kliendi, mis vĂ”imaldas mĂ€rkimisvÀÀrselt suurendada lugemisoperatsioonide kiirus.

Kuid enne, kui liigume edasiste muudatuste juurde, tuleks rÀÀkida piirangutest, mida on pÔhimÔtteliselt vÔimatu mööda hiilida, kui kasutada HDD-d.
Miks HDD-d ja kiire Random Access lugemine on ĂŒhilduvad
Nagu teada, salvestavad HBase ja paljud muud andmebaasid andmeid plokkidena, mille suurus on mitu kĂŒmmet kilobaiti. Vaikimisi on see umbes 64 KB. Kujutame nĂŒĂŒd ette, et peame vĂ€lja tooma vaid 100 baiti ja palume HBase'il anda meile need andmed mingi vĂ”tme alusel. Kuna HFile'ide ploki suurus on 64 KB, siis kĂŒsitud andmeid on 640 korda rohkem (vĂ€heke!) kui vajalik.
Edasi minnes, kuna pÀring lÀheb lÀbi HDFS-i ja selle metaandmete vahemÀlu mehhanismi ShortCircuitCache (mis vÔimaldab faile otseselt juurde pÀÀseda), toob see kaasa 1 MB lugemise kettalt. Siiski saab seda reguleerida parameetriga dfs.client.read.shortcircuit.buffer.size ja paljudel juhtudel on mÔistlik seda vÀÀrtust vÀhendada, nÀiteks 126 KB-ni.
Oletame, et me teeme nii, kuid lisaks, kui hakkame andmeid lugema java API kaudu, kasutades selliseid funktsioone nagu FileChannel.read ja palume operatsioonisĂŒsteemil lugeda mÀÀratud maht andmeid, loeb see "igaks juhuks" 2 korda rohkem, st 256 KB meie puhul. See juhtub seetĂ”ttu, et javasse pole lihtsat vĂ”imalust seada lippu FADV_RANDOM, mis takistaks sellist kĂ€itumist.
KokkuvÔttes, et saada meie 100 bait, loetakse taustal 2600 korda rohkem. Tundub, et lahendus on ilmne, vÀhendame ploki suurust kilobaidi vÔrra, seadke mainitud lipp ja saavutame suure selguse kiiruskasvu. Kuid probleem on selles, et ploki suuruse kahekordne vÀhendamine vÀhendab ka aega, mil me loeme baiti, samuti kaks korda.
MĂ”ningast kasu FADV_RANDOM lippu seadmisest vĂ”ib saada, kuid ainult suure mitme niidi korral ja ploki suuruse korral alates 128 Kb, kuid see on maksimaalselt paarikĂŒmmend protsenti:

Testid viidi lĂ€bi 100 failiga, igaĂŒks suurusega 1 Gb ja paigutatud 10 HDD kettale.
Arvutame, milleks me sellise kiirusena ĂŒldse loota saame:
Oletame, et me loeme 10 kettalt kiirusel 280 MB/s, st 3 miljonit korda 100 baiti. Kuid nagu me mÀletame, vajame me andmeid, mis esinevad 2600 korda vÀhem kui loetud. Seega jagame 3 miljonit 2600-ga ja saame 1100 kirjet sekundis.
Pettumust tekitav, eks ole? Nii on lood Juhusliku ligipÀÀsuga andmetele HDD-l â olenemata ploki suurusest. See on fĂŒĂŒsiline piir juhuslikule juurdepÀÀsule, ega ĂŒkski andmebaas ei suuda sellistes tingimustes rohkem vĂ€lja pigistada.
Kuidas siis andmebaasid saavutavad palju kĂ”rgemat kiirust? Sellele kĂŒsimusele vastamiseks vaatame, mis toimub jĂ€rgmises pildis:

Siin nĂ€eme, et esimesed paar minutit saavutatakse tĂ”epoolest kiirus umbes tuhat salvestust sekundis. Siiski, kuna loetakse palju rohkem, kui on kĂŒsitud, ladestuvad andmed Linuxi operatsioonisĂŒsteemi buff/cache'i ja kiirus tĂ”useb vastuvĂ”etavaks 60 tuhande juurde sekundis.
Seega uurime edasi juurdepÀÀsu kiirendamist ainult nendele andmetele, mis on OS-i vahemÀlus vÔi sarnastes SSD/NVMe salvestites, mille ligipÀÀsukiirus on vÔrdne.
Meie puhul teostame teste nelja serveriga keskkonnas, kus igaĂŒhes on jĂ€rgmised seadistused:
CPU: Xeon E5-2680 v4 @ 2.40GHz, 64 lÔime.
MĂ€lu: 730 GB.
java versioon: 1.8.0_111
Ja siin on tegelikult peamine punkt â andmemaht tabelites, mida tuleb lugeda. Asja on selles, et kui lugeda andmeid tabelist, mis mahub tĂ€ielikult HBase'i vahemĂ€llu, siis ei jĂ”ua lugemine isegi OS-i buff/cache'i. Kuna HBase eraldab vaikimisi 40% mĂ€last struktuurile nimega BlockCache. Tegelikult on see ConcurrentHashMap, kus vĂ”tme moodustab faili nimi + plokkide offset, ja vÀÀrtus on tegelikult andmed selle nihke kohta.
Seega, kui lugemine toimub ainult sellest struktuurist, siis me , nagu miljonit pÀringut sekundis. Ent kujutame ette, et me ei saa anda sadu gigabaiti mÀlu ainult andmebaasi vajadustele, kuna nende serverite peal töötab veel palju muud kasulikku.
NĂ€iteks meie puhul on BlockCache ĂŒhe RS-i maht umbes 12 GB. Oleme istutanud kaks RS-i ĂŒhele nodeâile, st BlockCacheâi jaoks on eraldatud kokku 96 GB kĂ”igis node'ides. Andmeid on aga selles osas palju rohkem, oletame, et meil on 4 tabelit, 130 regioonis, kus failide suurused on 800 MB, kokkusurutud FAST_DIFF, st kokku 410 GB (need on puhtad andmed, st ilma replikatsiooni tegurita).
Seega moodustab BlockCache vaid umbes 23% andmete kogumahtust ja see on palju lĂ€hemal tegelikele tingimustele, mida nimetatakse BigData-ks. Ja siin hakkab asi huvitavaks minema â kuna on selge, et mida vĂ€hem on pÀÀseda vahemĂ€llu, seda halvem on jĂ”udlus. Kui juhtub möödalask, tuleb teha palju tööd â st alla minna sĂŒsteemifunktsioonide kutsumiseni. Kuid seda ei saa vĂ€ltida, seega vaatame hoopis teist aspekti â mis juhtub andmetega vahemĂ€lus?
Lihtsustame olukorda ja eeldame, et meil on vahemÀlu, kuhu saab paigutada ainult 1 objekti. Siin on nÀide sellest, mis juhtub, kui proovi töödelda andmeid, mis on 3 korda suuremad kui vahemÀlu, peame me:
1. Paigutame ploki 1 vahemÀllu
2. Eemaldame ploki 1 vahemÀlust
3. Paigutame ploki 2 vahemÀllu
4. Eemaldame ploki 2 vahemÀlust
5. Paigutame ploki 3 vahemÀllu
Oleme teinud 5 toimingut! Kuid selle olukorra nimetamist normaalseks ei saa, tegelikult sundime HBase'i tegema palju tĂ€iesti mĂ”ttetut tööd. Ta pidevalt loeb andmeid OS vahemĂ€lust, paigutab selle BlockCache'i, et peaaegu kohe see vĂ€lja visata, kuna saabub uus andmepartii. Postituse alguses olev animatsioon nĂ€itab probleemi olemust â prĂŒgikogujate koormus ĂŒletab piire, atmosfÀÀr soojeneb, vĂ€ike Greta kauges ja kuumas Rootsis tunneb muret. Ja meie, IT-inimesed, ei meeldi, kui lapsed on kurvad, seega hakkame mĂ”tlema, mida selle olukorraga teha.
Mis siis, kui paigutame vahemÀllu mitte kÔik plokid, vaid ainult teatud protsendi neist, nii et vahemÀlu ei tÀituks? Alustame lihtsalt lisades mÔned koodirida BlockCache'i andmete paigutamise funktsiooni algusesse:
public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory) {
if (cacheDataBlockPercent != 100 && buf.getBlockType().isData()) {
if (cacheKey.getOffset() % 100 >= cacheDataBlockPercent) {
return;
}
}
...
Siin on mĂ”te jĂ€rgmine: offset on ploki asukoht failis ja viimased numbrid jaotuvad juhuslikult ja ĂŒhtlaselt vahemikus 00 kuni 99. SeetĂ”ttu jĂ€tame vahele ainult need, mis jÀÀvad meie soovitud vahemikku.
NĂ€iteks seadistame cacheDataBlockPercent = 20 ja vaatame, mis juhtub:

Tulemus on ilmne. Allpool toodud diagrammid nĂ€itavad, kuidas see kiirus saavutati â me sÀÀstame tohutult GC ressursse, vĂ€ltides sisefilosoofilist tööd andmete paigutamisel vahemĂ€llu, et neid kohe tĂ”ugata marslaste koertele jalge alla:

CPU kasutamine suureneb, kuid siiski mitte oluliselt rohkem kui tulemuslikkus:

Samuti tasub mainida, et BlockCache'is hoitavad plokid vĂ”ivad olla erinevad. Suur osa, umbes 95%, on tegelikud andmed. ĂlejÀÀnud on metaandmed, nagu nĂ€iteks Bloom filtrid vĂ”i LEAF_INDEX ja . Need to ensure that the data is well-tested and useful, as before directly accessing the data, HBase checks the meta to understand if it should search further, and if so, where the required block is located.
Therefore, in the code, we see the condition check. buf.getBlockType().isData() and thanks to this meta, we will keep it in the cache anyway.
Now let's increase the load and also slightly enhance the feature. In the first test, we set the trimming percentage to 20, and BlockCache was slightly underloaded. Now we'll set it to 23% and add 100 threads every 5 minutes to see when saturation occurs:

Here we see that the original version hits a ceiling almost immediately at around 100,000 requests per second. Meanwhile, the patch provides a speedup to 300,000. It's clear that further acceleration is not as 'free,' and CPU utilization also rises.
Kuid see pole kuigi elegantne lahendus, kuna me ei tea ette, kui suur protsent plokkidest tuleb vahemÀlus hoida; see sÔltub koormusprofiilist. SeetÔttu on rakendatud mehhanism, mis kohandab automaatselt seda parameetrit vastavalt lugemistegevuse aktiivsusele.
Selle haldamiseks on lisatud kolm parameetrit:
hbase.lru.cache.heavy.eviction.count.limit â mÀÀrab, kui mitu korda peab toimuma andmete eemaldamise protsess vahemĂ€lust, enne kui hakkame kasutama optimeerimist (st plokkide vahelejĂ€tmist). Vaikimisi on see MAX_INT = 2147483647, mis tegelikult tĂ€hendab, et funktsioon ei hakka kunagi tööle, kui see vÀÀrtus jÀÀb selliseks. Kuna andmete eemaldamise protsess toimub iga 5-10 sekundi jĂ€rel (sĂ”ltuvalt koormusest) ja 2147483647 * 10 / 60 / 60 / 24 / 365 = 680 aastat. Kuid me saame seada selle parameetri nulliks ja sundida funktsiooni tööle kohe pĂ€rast kĂ€ivitamist.
Kuid selle parameetri puhul on ka kasulikku koormust. Kui meie koormuse iseloom on selline, et lĂŒhiajaliselt vahelduvad pidevalt lugemised (öelda pĂ€evasel ajal) ja pikaajalised (öösel), siis saame teha nii, et funktsioon kĂ€ivitub ainult siis, kui toimuvad pikaajalised lugemistegevused.
NĂ€iteks teame, et lĂŒhiajalised lugemised kestavad tavaliselt umbes 1 minuti. Siiski ei tohi hakata blokke eemaldama, sest vahemĂ€lu ei jĂ”ua aeguda ja seetĂ”ttu saame seadistada selle parameetri nĂ€iteks 10-ks. See tĂ€hendab, et optimeerimine hakkab töötama alles siis, kui on alanud pikaajaline aktiivne lugemine, st 100 sekundi pĂ€rast. Seega, kui meil on lĂŒhiajaline lugemine, siis kĂ”ik blokid jÀÀvad vahemĂ€llu ja on kergesti kĂ€ttesaadavad (vĂ€lja arvatud need, mis kĂ”rvaldavad tavalise algoritmiga). Ja kui teeme pikaajalisi lugemisi, siis funktsioon aktiveeritakse ja saavutame palju kĂ”rgema jĂ”udluse.
hbase.lru.cache.heavy.eviction.mb.size.limit â mÀÀrab, kui palju megabaite soovime 10 sekundi jooksul vahemĂ€llu panna (ja loomulikult vabastada). Funktsioon ĂŒritab saavutada seda vÀÀrtust ja hoida seda. Asi on selles, et kui paneme vahemĂ€llu gigabaitide kaupa, siis tuleb ka gigabaitide kaupa vabastada, mis, nagu me ĂŒlal nĂ€gime, on ĂŒsna kulukas. Siiski pole mĂ”tet proovida seada seda vÀÀrtust liiga madalaks, kuna see toob kaasa varajase vĂ€ljumise plokkskeemide vahelejĂ€tmise reĆŸiimist. VĂ”imsate serverite (umbes 20â40 fĂŒĂŒsilist sĂŒdamikku) jaoks on optimaalne seada umbes 300â400 MB. Keskmise klassi (umbes 10 sĂŒdamikku) jaoks 200â300 MB. NĂ”rkade sĂŒsteemide (2â5 sĂŒdamikku) puhul vĂ”iks olla normaalseks 50â100 MB (sellistel ei ole testitud).
Vaatame, kuidas see töötab: oletame, et oleme seadnud hbase.lru.cache.heavy.eviction.mb.size.limit = 500, toimub mingi koormus (lugemine) ja siis iga ~10 sekundi jÀrel arvutame, kui palju baite on vabastatud jÀrgmise valemi jÀrgi:
Overhead = Vabastatud baitide summa (MB) * 100 / Limite (MB) â 100;
Kui tegelikult on vabastatud 2000 MB, siis Overhead on jÀrgmine:
2000 * 100 / 500 â 100 = 300%
Algrithmid pĂŒĂŒavad sĂ€ilitada mitte rohkem kui paarikĂŒmne protsendi ulatuses, seega vĂ€hendab see omadus vahemĂ€lu blokke, rakendades sellega automaatse hÀÀlestamise mehhanismi.
Kuid kui koormus on langenud, nÀiteks kui vabanes vaid 200 MB ja Overhead muutus negatiivseks (nii-öelda overshooting):
200 * 100 / 500 â 100 = -60%
Siis see omadus vastupidi suurendab vahemÀlu blokke kuni Overhead ei muutu positiivseks.
Allpool on nĂ€ide sellest, kuidas see vĂ€lja nĂ€eb reaalsetel andmetel. Ărge proovige saavutada 0%, see on vĂ”imatu. AastakĂŒmne jooksul 30â100% on juba vĂ€ga hea, kuna see aitab vĂ€ltida enneaegset vĂ€ljumist optimeerimise reĆŸiimist lĂŒhiajaliste tĂ”usude tĂ”ttu.
hbase.lru.cache.heavy.eviction.overhead.coefficient â mÀÀrab, kui kiiresti me tahame tulemusi saada. Kui me teame kindlalt, et meie lugemised on enamasti pikad ja me ei taha oodata, vĂ”ime seda koefitsienti suurendada ja saavutada kĂ”rge jĂ”udluse kiiremini.
NĂ€iteks seadsime selle koefitsiendi = 0,01. See tĂ€hendab, et ĂŒleminek (vt ĂŒle) korrutatakse selle arvuga saadud tulemuste nimel ning vĂ€heneb mÀÀratud vaheoste protsent. Oletame, et ĂŒleminek = 300% ja koefitsient = 0,01, siis vaheoste protsent vĂ€heneb 3%.
Sarnane âtagasivooluâ loogika on rakendatud ka negatiivsete ĂŒlemise vÀÀrtuste (ĂŒleslaundmine) jaoks. Kuna lĂŒhiajalised kĂ”ikumised lugemiste-volatiilsuse osas on alati vĂ”imalikud, vĂ”imaldab see mehhanism vĂ€ltida enneaegset vĂ€ljumist optimeerimisseisundist. Tagasivoolu loogika on vastupidine: mida tugevam on ĂŒlelaundmine, seda rohkem on vaheoste salvestatud.

Rakenduskood
LruBlockCache cache = this.cache.get();
if (cache == null) {
break;
}
freedSumMb += cache.evict() / 1024 / 1024;
/*
* MÔnikord loeme me rohkem andmeid, kui BlockCache'i mahub
* ja see pÔhjustab kÔrget evakueerimise mÀÀra.
* See omakorda viib intensiivse prĂŒgikoguja tööni.
* Nii et palju plokke pannakse BlockCache'i, kuid neid ei loeta,
* kuid nad kulutavad palju CPU ressursse.
* Siin analĂŒĂŒsime, kui palju baitide vabastamine toimus ja otsustame,
* kas on aeg vÀhendada vahemÀlu plokkide arvu.
* See aitab vÀltida liiga paljude plokkide lisamist BlockCache'i,
* kui evict() töötab vÀga aktiivselt ja sÀÀstab CPU-d teiste tööde jaoks.
* Rohkem ĂŒksikasju: https://issues.apache.org/jira/browse/HBASE-23887
*/
// Enne kÔike peame kontrollima, kui palju aega
// on möödunud eelmise evict() kÀivitamisest
// See peaks olema peaaegu sama aeg (+/- 10s)
// kuna saame sarnaseid vabastatud baitide mahtusid iga kord.
// 10s, sest see on vaikimisi periood evict() kĂ€ivitamiseks (vt ĂŒlalpool this.wait)
long stopTime = System.currentTimeMillis();
if ((stopTime - startTime) > 1000 * 10 - 1) {
// Siin peame arvutama, milline olukord meil on.
// Meil on piir "hbase.lru.cache.heavy.eviction.bytes.size.limit"
// ja saame selle ĂŒleliigse arvutada.
// Kasutame seda teavet otsustamiseks,
// kuidas muuta vahemÀlu plokkide protsenti.
freedDataOverheadPercent =
(int) (freedSumMb * 100 / cache.heavyEvictionMbSizeLimit) - 100;
if (freedSumMb > cache.heavyEvictionMbSizeLimit) {
// NĂŒĂŒd oleme olukorras, kus oleme ĂŒle piiri
// Aga vĂ”ib-olla ignoreerime seda, sest see lĂ”peb ĂŒsna pea
heavyEvictionCount++;
if (heavyEvictionCount > cache.heavyEvictionCountLimit) {
// See kestab kaua ja me peame nĂŒĂŒd vĂ€hendama vahemĂ€lu
// plokkide arvu. Arvutame siin, kui palju plokke tahame vahele jÀtta.
// See sÔltub:
// 1. Ăleliigsest - kui ĂŒleliigne on suur, saame olla agressiivsemad
// vahemÀlu plokkide arvu vÀhendamisel.
// 2. Kui kiiresti soovime tulemust saada. Kui teame, et meie
// intensiivne lugemine kestab kaua, ei soovi me oodata ja saame
// suurendada koefitsienti ja saada head jĂ”udlust ĂŒsna kiiresti.
// Kuid kui me ei ole kindel, saame seda aeglaselt teha ja see vÔib
// takistada enneaegset vĂ€ljumist sellest reĆŸiimist. Nii et kui koefitsient on
// kÔrgem, saame paremat jÔudlust, kui intensiivne lugemine on stabiilne.
// Kuid kui lugemine muutub, saame sellele kohanduda ja seada
// koefitsienti madalamale vÀÀrtusele.
int change =
(int) (freedDataOverheadPercent * cache.heavyEvictionOverheadCoefficient);
// Kuid praktika nÀitab, et 15% vÀhendamine on tÀiesti piisav.
// Me ei ole ahned (see vÔib viia enneaegse vÀljumiseni).
change = Math.min(15, change);
change = Math.max(0, change); // Ma arvan, et see ei juhtu kunagi, kuid kontrolli siiski
// Niisiis on see peamine punkt, siin vÀhendame vahemÀlu plokkide %
cache.cacheDataBlockPercent -= change;
// Kui me lÀheme liiga madalale, peame siin peatuma, 1% peaks igal juhul olema.
cache.cacheDataBlockPercent = Math.max(1, cache.cacheDataBlockPercent);
}
} else {
// Noh, me oleme ĂŒletanud.
// VĂ”ib-olla on see lihtsalt lĂŒhiajaline kĂ”ikumine ja saame selles reĆŸiimis jÀÀda.
// See aitab vĂ€ltida enneaegset vĂ€ljumist lĂŒhiajalise kĂ”ikumise ajal.
// Kui ĂŒletamine on alla 90%, proovime tĂ”sta vahemĂ€lu
// plokkide protsenti ja loodame, et seda on piisavalt.
if (freedSumMb >= cache.heavyEvictionMbSizeLimit * 0.1) {
// Lihtne loogika: rohkem ĂŒletamist - rohkem vahemĂ€lu plokke (tagasisurve)
int change = (int) (-freedDataOverheadPercent * 0.1 + 1);
cache.cacheDataBlockPercent += change;
// Kuid see ei saa olla rohkem kui 100%, nii et kontrollime seda.
cache.cacheDataBlockPercent = Math.min(100, cache.cacheDataBlockPercent);
} else {
// Tundub, et intensiivne lugemine on lÀbi.
// Lihtsalt lahkume sellest reĆŸiimist.
heavyEvictionCount = 0;
cache.cacheDataBlockPercent = 100;
}
}
LOG.info("BlockCache evicted (MB): {}, overhead (%): {}, " +
"heavy eviction counter: {}, " +
"current caching DataBlock (%): {}",
freedSumMb, freedDataOverheadPercent,
heavyEvictionCount, cache.cacheDataBlockPercent);
freedSumMb = 0;
startTime = stopTime;
}
Vaatame nĂŒĂŒd seda kĂ”ike reaalse nĂ€ite pealt. Meil on jĂ€rgmine teststsenaarium:
- Alustame skaneerimise tegemisega (25 lÔime, partii = 100)
- Viie minuti pÀrast lisame multi-get'id (25 lÔime, partii = 100)
- Viie minuti pĂ€rast lĂŒlitame multi-get'id vĂ€lja (jĂ€tkame jĂ€lle ainult skaneerimisega)
Teeme kaks lĂ€bimist, esmalt hbase.lru.cache.heavy.eviction.count.limit = 10000 (mis tegelikult lĂŒlitab funktsiooni vĂ€lja), seejĂ€rel seadistame limiidi = 0 (lĂŒlitab sisse).
Logides allpool nÀeme, kuidas funktsioon aktiveerub, vÀhendades overshooting'i 14-71%ni. Aeg-ajalt koormus vÀheneb, mis aktiveerib backpressure'i ja HBase salvestab jÀlle rohkem plokke.
RegionServeri logi
evicted (MB): 0, suhe 0.0, ĂŒlejÀÀk (%): -100, raske vĂ€ljaheitmise loendur: 0, praegune salvestamine DataBlock (%): 100
evicted (MB): 0, suhe 0.0, ĂŒlejÀÀk (%): -100, raske vĂ€ljaheitmise loendur: 0, praegune salvestamine DataBlock (%): 100
evicted (MB): 2170, suhe 1.09, ĂŒlejÀÀk (%): 985, raske vĂ€ljaheitmise loendur: 1, praegune salvestamine DataBlock (%): 91 < start
evicted (MB): 3763, suhe 1.08, ĂŒlejÀÀk (%): 1781, raske vĂ€ljaheitmise loendur: 2, praegune salvestamine DataBlock (%): 76
evicted (MB): 3306, suhe 1.07, ĂŒlejÀÀk (%): 1553, raske vĂ€ljaheitmise loendur: 3, praegune salvestamine DataBlock (%): 61
evicted (MB): 2508, suhe 1.06, ĂŒlejÀÀk (%): 1154, raske vĂ€ljaheitmise loendur: 4, praegune salvestamine DataBlock (%): 50
evicted (MB): 1824, suhe 1.04, ĂŒlejÀÀk (%): 812, raske vĂ€ljaheitmise loendur: 5, praegune salvestamine DataBlock (%): 42
evicted (MB): 1482, suhe 1.03, ĂŒlejÀÀk (%): 641, raske vĂ€ljaheitmise loendur: 6, praegune salvestamine DataBlock (%): 36
evicted (MB): 1140, suhe 1.01, ĂŒlejÀÀk (%): 470, raske vĂ€ljaheitmise loendur: 7, praegune salvestamine DataBlock (%): 32
evicted (MB): 913, suhe 1.0, ĂŒlejÀÀk (%): 356, raske vĂ€ljaheitmise loendur: 8, praegune salvestamine DataBlock (%): 29
vĂ€lja visatud (MB): 912, suhe 0.89, ĂŒlejÀÀk (%): 356, raske vĂ€lja viskamise konto: 9, praegune caching DataBlock (%): 26
vĂ€lja visatud (MB): 684, suhe 0.76, ĂŒlejÀÀk (%): 242, raske vĂ€lja viskamise konto: 10, praegune caching DataBlock (%): 24
vĂ€lja visatud (MB): 684, suhe 0.61, ĂŒlejÀÀk (%): 242, raske vĂ€lja viskamise konto: 11, praegune caching DataBlock (%): 22
vĂ€lja visatud (MB): 456, suhe 0.51, ĂŒlejÀÀk (%): 128, raske vĂ€lja viskamise konto: 12, praegune caching DataBlock (%): 21
vĂ€lja visatud (MB): 456, suhe 0.42, ĂŒlejÀÀk (%): 128, raske vĂ€lja viskamise konto: 13, praegune caching DataBlock (%): 20
vĂ€lja visatud (MB): 456, suhe 0.33, ĂŒlejÀÀk (%): 128, raske vĂ€lja viskamise konto: 14, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, raske vĂ€lja viskamise konto: 15, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 342, suhe 0.32, ĂŒlejÀÀk (%): 71, raske vĂ€lja viskamise konto: 16, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 342, suhe 0.31, ĂŒlejÀÀk (%): 71, raske vĂ€lja viskamise konto: 17, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.3, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 18, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.29, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 19, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.27, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 20, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.25, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 21, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.24, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 22, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.22, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 23, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.21, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 24, praegune caching DataBlock (%): 19
vĂ€lja visatud (MB): 228, suhe 0.2, ĂŒlejÀÀk (%): 14, raske vĂ€lja viskamise konto: 25, praegune caching DataBlock (%): 19
evikt (MB): 228, suhe 0.17, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 26, praegune vahemĂ€lu DataBlock (%): 19
evikt (MB): 456, suhe 0.17, ĂŒlejÀÀk (%): 128, raske evakuatsioonikonto: 27, praegune vahemĂ€lu DataBlock (%): 18 < lisatud saadud (aga tabel sama)
evikt (MB): 456, suhe 0.15, ĂŒlejÀÀk (%): 128, raske evakuatsioonikonto: 28, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 342, suhe 0.13, ĂŒlejÀÀk (%): 71, raske evakuatsioonikonto: 29, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 342, suhe 0.11, ĂŒlejÀÀk (%): 71, raske evakuatsioonikonto: 30, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 342, suhe 0.09, ĂŒlejÀÀk (%): 71, raske evakuatsioonikonto: 31, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 228, suhe 0.08, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 32, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 228, suhe 0.07, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 33, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 228, suhe 0.06, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 34, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 228, suhe 0.05, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 35, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 228, suhe 0.05, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 36, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 228, suhe 0.04, ĂŒlejÀÀk (%): 14, raske evakuatsioonikonto: 37, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 109, suhe 0.04, ĂŒlejÀÀk (%): -46, raske evakuatsioonikonto: 37, praegune vahemĂ€lu DataBlock (%): 22 < tagasitĂ”ukerĂ”hk
evikt (MB): 798, suhe 0.24, ĂŒlejÀÀk (%): 299, raske evakuatsioonikonto: 38, praegune vahemĂ€lu DataBlock (%): 20
evikt (MB): 798, suhe 0.29, ĂŒlejÀÀk (%): 299, raske evakuatsioonikonto: 39, praegune vahemĂ€lu DataBlock (%): 18
evikt (MB): 570, suhe 0.27, ĂŒlejÀÀk (%): 185, raske evakuatsioonikonto: 40, praegune vahemĂ€lu DataBlock (%): 17
evikt (MB): 456, suhe 0.22, ĂŒlejÀÀk (%): 128, raske evakuatsioonikonto: 41, praegune vahemĂ€lu DataBlock (%): 16
evicted (MB): 342, ratio 0.16, overhead (%): 71, heavy eviction counter: 42, current caching DataBlock (%): 16
evicted (MB): 342, ratio 0.11, overhead (%): 71, heavy eviction counter: 43, current caching DataBlock (%): 16
evicted (MB): 228, ratio 0.09, overhead (%): 14, heavy eviction counter: 44, current caching DataBlock (%): 16
evicted (MB): 228, ratio 0.07, overhead (%): 14, heavy eviction counter: 45, current caching DataBlock (%): 16
evicted (MB): 228, ratio 0.05, overhead (%): 14, heavy eviction counter: 46, current caching DataBlock (%): 16
evicted (MB): 222, ratio 0.04, overhead (%): 11, heavy eviction counter: 47, current caching DataBlock (%): 16
evicted (MB): 104, ratio 0.03, overhead (%): -48, heavy eviction counter: 47, current caching DataBlock (%): 21 < interrupt gets
evicted (MB): 684, ratio 0.2, overhead (%): 242, heavy eviction counter: 48, current caching DataBlock (%): 19
evicted (MB): 570, ratio 0.23, overhead (%): 185, heavy eviction counter: 49, current caching DataBlock (%): 18
evicted (MB): 342, ratio 0.22, overhead (%): 71, heavy eviction counter: 50, current caching DataBlock (%): 18
evicted (MB): 228, ratio 0.21, overhead (%): 14, heavy eviction counter: 51, current caching DataBlock (%): 18
evicted (MB): 228, ratio 0.2, overhead (%): 14, heavy eviction counter: 52, current caching DataBlock (%): 18
evicted (MB): 228, ratio 0.18, overhead (%): 14, heavy eviction counter: 53, current caching DataBlock (%): 18
evicted (MB): 228, ratio 0.16, overhead (%): 14, heavy eviction counter: 54, current caching DataBlock (%): 18
evicted (MB): 228, ratio 0.14, overhead (%): 14, heavy eviction counter: 55, current caching DataBlock (%): 18
evicted (MB): 112, ratio 0.14, overhead (%): -44, heavy eviction counter: 55, current caching DataBlock (%): 23 < back pressure
evicted (MB): 456, ratio 0.26, overhead (%): 128, heavy eviction counter: 56, current caching DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.31, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 57, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 58, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 59, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 60, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 61, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 62, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 63, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.32, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 64, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 65, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 66, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.32, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 67, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 68, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.32, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 69, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.32, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 70, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 71, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 72, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja aetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljaheitmise number: 73, praegune vahemĂ€lu DataBlock (%): 22
vĂ€lja tĂ”stetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljajĂ€tmist loendur: 74, praegune puhverdatud DataBlock (%): 22
vĂ€lja tĂ”stetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljajĂ€tmist loendur: 75, praegune puhverdatud DataBlock (%): 22
vĂ€lja tĂ”stetud (MB): 342, suhe 0.33, ĂŒlejÀÀk (%): 71, suur vĂ€ljajĂ€tmist loendur: 76, praegune puhverdatud DataBlock (%): 22
vĂ€lja tĂ”stetud (MB): 21, suhe 0.33, ĂŒlejÀÀk (%): -90, suur vĂ€ljajĂ€tmist loendur: 76, praegune puhverdatud DataBlock (%): 32
evicted (MB): 0, suhe 0.0, ĂŒlejÀÀk (%): -100, raske vĂ€ljaheitmise loendur: 0, praegune salvestamine DataBlock (%): 100
evicted (MB): 0, suhe 0.0, ĂŒlejÀÀk (%): -100, raske vĂ€ljaheitmise loendur: 0, praegune salvestamine DataBlock (%): 100
Skaalide eesmĂ€rk oli nĂ€idata sama protsessi graafiku kujul, kajastades suhet kahe puhvri eraldise vahel â single (kuhu jĂ”uavad plokid, mida keegi kunagi ei ole kĂŒsinud) ja multi (siin hoitakse andmeid, mis on vĂ€hemalt kord nĂ”udmisest tulnud):

Ja lĂ”puks, kuidas nĂ€evad vĂ€lja parameetrite töö graafiku kujul. VĂ”rdluseks, puhver oli alguses tĂ€ielikult vĂ€lja lĂŒlitatud, seejĂ€rel kĂ€ivitati HBase puhverdamisega ja optimiseerimise alustamise viivitus 5 minuti vĂ”rra (30 vĂ€lja tĂ”stmist tsĂŒklit).
TĂ€ieliku koodi leiate Pull Request'ist githubis.
Kuid 300 tuhat lugemist sekundis ei ole kĂ”ik, mida selle riistvara kohta antud tingimustes saavutada on vĂ”imalik. Asi on selles, et andmete kĂŒsi puhul HDFS-i kaudu kasutatakse ShortCircuitCache (edaspidi SSC) mehhanismi, mis vĂ”imaldab andmetele otse juurde pÀÀseda, vĂ€ltides vĂ”rgu interaktsioone.
Profiliseerimine nÀitas, et see mehhanism, kuigi toob suurt kasu, muutub mingil hetkel kitsaskohaks, kuna praktiliselt kÔik rasked operatsioonid toimuvad lock'i sees, mis viib suure osa ajast ummikseisudeni.

Seda mÔistdes, saime aru, et probleemi saab vÀltida, luues sÔltumatute SSC massiivi:
private final ShortCircuitCache[] shortCircuitCache;
...
shortCircuitCache = new ShortCircuitCache[this.clientShortCircuitNum];
for (int i = 0; i < this.clientShortCircuitNum; i++)
this.shortCircuitCache[i] = new ShortCircuitCache(âŠ);
Ja seejĂ€rel töödelda neid, vĂ€listades ĂŒksteisega kattumise viimasest digiti offset'i jĂ€rgi:
public ShortCircuitCache getShortCircuitCache(long idx) {
return shortCircuitCache[(int) (idx % clientShortCircuitNum)];
}
NĂŒĂŒd on vĂ”imalik alustada katsetega. Selleks loeme HDFS-ist faile lihtsa mitme lĂ”imega rakendusega. Seadistame parameetrid:
conf.set("dfs.client.read.shortcircuit", "true");
conf.set("dfs.client.read.shortcircuit.buffer.size", "65536"); // vaikimisi = 1 MB ja see aeglustab lugemist oluliselt, seega on parem kohandada vastavalt tegelikele vajadustele
conf.set("dfs.client.short.circuit.num", num); // vahemikus 1 kuni 10
Ja lihtsalt loeme faile:
FSDataInputStream in = fileSystem.open(path);
for (int i = 0; i 900000000)
position = 0L;
int res = in.read(position, byteBuffer, 0, 65536);
}
See kood kĂ€ib eraldi lĂ”imedena ja me suurendame samal ajal loetavate failide arvu (10 kuni 200 â horisontaalne telg) ja vahemĂ€lude arvu (1 kuni 10 â graafikud). Vertikaalne telg nĂ€itab kiiruskasvu, mis tuleneb SSC arvu suurendamisest vĂ”rreldes olukorraga, kus vahemĂ€lu on ainult ĂŒks.

Kuidas graafikut lugeda: 100 000 lugemise tĂ€itmine 64 KB plokkidena ĂŒhe vahemĂ€luga vĂ”tab aega 78 sekundit. Samas, viie vahemĂ€luga tĂ€itmine vĂ”tab aega 16 sekundit. See tĂ€hendab, et kiirus kasvab umbes 5 korda. Grafi kust on nĂ€ha, et vĂ€ikse arvu paralleelsete lugemiste korral ei ole mĂ”ju vĂ€ga mĂ€rgatav, see muutub olulise tegurina, kui lugemiste arv ĂŒletab 50. Samuti on mĂ€rgatav, et SSC arvu suurendamine 6-st ja rohkemast annab oluliselt vĂ€hem jĂ”udluse kasvu.
MĂ€rkus 1: kuna testimisresultsid on ĂŒsna volatiilsed (vt allpool), tehti 3 kĂ€ivitust ja saadud vÀÀrtused keskmistati.
MÀrkus 2: JÔudluse kasv juhusliku juurdepÀÀsu optimeerimise tÔttu on sama, kuigi juurdepÀÀs ise on veidi aeglasem.
Siiski tuleb mÀrkida, et erinevalt HBase'ist ei ole see kiirusetÔus alati tasuta. Siin pigem 'vabastame' CPU vÔimalusi, et töö tegemiseks, mitte et see jÀÀb lukku.

Siin vĂ”ib nĂ€ha, et ĂŒldiselt toob vahemĂ€lu arvu suurendamine kaasa umbes proportsionaalse kasvu CPU kasutuses. Siiski on mĂ”ned paremad kombinatsioonid.
Vaatame nÀiteks hoolikamalt seadistust SSC = 3. Tootlikkuse kasv vahemikus on umbes 3,3 korda. Allpool on tulemused kÔikide kolme eraldi kÀivituse kohta.

Samas CPU tarbimine kasvab umbes 2,8 korda. Erinevus ei ole eriti suur, kuid vĂ€ikese Greta jaoks on see juba rÔÔm ja vĂ”ib-olla tekib aega kooli ja tundide kĂŒlastamiseks.
Seega peaksite see olema positiivne mĂ”ju igale tööriistale, mis kasutab massi pÀÀsu HDFS-ile (nĂ€iteks Spark jne), tingimusel, et rakenduskood on kerge (st ummistus on just HDFS kliendi poolel) ja CPU-l on vabade ressursside varu. Testimiseks vaatame, milline mĂ”ju on BlockCache'i optimeerimise ja SSC hÀÀlestamise ĂŒhisrakendamisel HBase'ist lugemisel.

Siit nĂ€htub, et sellistes tingimustes pole efekt nii suur kui rafineeritud testides (lugemine ilma igasuguse töötlemiseta), kuid siit on tĂ€iesti vĂ”imalik teha lisaks 80K. Ăheskoos annavad mĂ”lemad optimeerimised kuni 4 korda kiirusetĂ”usu.
Selle optimeerimise jaoks tehti ka PR , mis liideti ning see funktsionaalsus on saadaval jÀrgmistes vÀljaannetes.
Ja lÔpuks oli huvitav vÔrrelda sarnaste wide-column andmebaaside Cassandra ja HBase lugemisvÔimet.
Selleks kÀivitasime YCSB koormustestimise standardkasutust kakselt hostilt (kokku 800 niiti). Serveripooles oli igal poole 4 RegionServeri ja Cassandra eksemplari 4 hostil (mitte nendel, kus kliendid töötavad, et vÀltida nende mÔju). Lugemised toimusid tabelitest, mille suurus oli:
HBase â 300 GB HDFS-is (100 GB puhaste andmete jaoks)
Cassandra â 250 GB (replikatsiooni faktor = 3)
T. e. maht oli enam-vÀhem sama (HBase-is veidi rohkem).
HBase parameetrid:
dfs.client.short.circuit.num = 5 (HDFS kliendi optimeerimine)
hbase.lru.cache.heavy.eviction.count.limit = 30 â see tĂ€hendab, et patch hakkab lĂ€bi töötama 30 vĂ€ljatĂ”ukamise jĂ€rel (~5 minutit)
hbase.lru.cache.heavy.eviction.mb.size.limit = 300 â sihtkogus vahemĂ€lu ja vĂ€ljatĂ”ukamine
YCSB logid on töödeldud ja koondatud Exceli graafikuteks:

Nagu nÀha, vÔimaldab optimeerimise andmed nende andmebaaside jÔudluse vÔrdsustada nendes tingimustes ja saavutada 450 tuhat lugemist sekundis.
Loodame, et see teave on kedagi kasulik meie pÔnevas vÔitluses jÔudluse nimel.
Allikas: habr.com
