Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Mikil afköst eru ein af lykilkröfunum þegar unnið er með stór gögn. Í gagnahleðsludeild Sberbank dælum við næstum öllum færslum inn í Hadoop gagnaskýið okkar og tökumst því á við mjög mikið upplýsingaflæði. Auðvitað erum við alltaf að leita að leiðum til að bæta árangur og nú viljum við segja þér hvernig okkur tókst að plástra RegionServer HBase og HDFS biðlarann, þökk sé þeim að við gátum aukið hraða lestraraðgerða verulega.
Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Hins vegar, áður en farið er að kjarna endurbótanna, er þess virði að tala um takmarkanir sem í grundvallaratriðum er ekki hægt að sniðganga ef þú situr á HDD.

Hvers vegna harður diskur og hraður lestur af handahófi eru ósamrýmanlegur
Eins og þú veist, HBase, og margir aðrir gagnagrunnar, geyma gögn í blokkum sem eru nokkrar tugir kílóbæta að stærð. Sjálfgefið er það um 64 KB. Nú skulum við ímynda okkur að við þurfum að fá aðeins 100 bæti og við biðjum HBase að gefa okkur þessi gögn með því að nota ákveðinn lykil. Þar sem blokkastærðin í HFiles er 64 KB verður beiðnin 640 sinnum stærri (bara mínúta!) en nauðsynlegt er.

Næst, þar sem beiðnin mun fara í gegnum HDFS og skyndiminniskerfi lýsigagna ShortCircuitCache (sem leyfir beinan aðgang að skrám), þetta leiðir til þess að þegar lesið er 1 MB af disknum. Hins vegar er hægt að stilla þetta með færibreytunni dfs.client.read.shortcircuit.buffer.stærð og í mörgum tilfellum er skynsamlegt að minnka þetta gildi, til dæmis í 126 KB.

Segjum að við gerum þetta, en þar að auki, þegar við byrjum að lesa gögn í gegnum Java API, eins og aðgerðir eins og FileChannel.read og biðjum stýrikerfið að lesa tilgreint gagnamagn, þá stendur það „bara ef“ 2 sinnum meira , þ.e. 256 KB í okkar tilviki. Þetta er vegna þess að Java hefur ekki auðveld leið til að stilla FADV_RANDOM fána til að koma í veg fyrir þessa hegðun.

Fyrir vikið, til að fá 100 bæti okkar, er 2600 sinnum meira lesið undir hettunni. Svo virðist sem lausnin sé augljós, við skulum minnka blokkastærðina niður í kílóbæti, stilla nefndan fána og fá mikla uppljómunarhröðun. En vandamálið er að með því að minnka blokkastærðina um 2 sinnum, fækkum við líka fjölda læstra bæta á tímaeiningu um 2 sinnum.

Nokkur ávinningur af því að setja FADV_RANDOM fánann er hægt að fá, en aðeins með háum fjölþráðum og með blokkastærð 128 KB, en þetta er að hámarki nokkra tugi prósenta:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Prófanir voru gerðar á 100 skrám, hver 1 GB að stærð og staðsett á 10 HDD.

Við skulum reikna út hvað við getum í grundvallaratriðum treyst á á þessum hraða:
Segjum að við lesum af 10 diskum á hraðanum 280 MB/sek, þ.e. 3 milljón sinnum 100 bæti. En eins og við munum eru gögnin sem við þurfum 2600 sinnum minni en það sem lesið er. Þannig deilum við 3 milljónum með 2600 og fáum 1100 færslur á sekúndu.

Þunglyndi, er það ekki? Það er náttúran Slembiaðgangur aðgangur að gögnum á HDD - óháð stærð blokkarinnar. Þetta eru líkamleg mörk handahófsaðgangs og enginn gagnagrunnur getur kreist meira út við slíkar aðstæður.

Hvernig ná gagnagrunnar þá miklu meiri hraða? Til að svara þessari spurningu skulum við skoða hvað er að gerast á eftirfarandi mynd:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Hér sjáum við að fyrstu mínúturnar er hraðinn í raun um þúsund met á sekúndu. En frekar, vegna þess að miklu meira er lesið en beðið var um, lenda gögnin í buff/cache stýrikerfisins (linux) og hraðinn eykst í sæmilegri 60 þúsund á sekúndu

Þannig munum við frekar takast á við að flýta aðgangi aðeins að gögnum sem eru í skyndiminni stýrikerfisins eða staðsett í SSD/NVMe geymslutækjum með sambærilegum aðgangshraða.

Í okkar tilviki munum við framkvæma próf á bekk með 4 netþjónum, sem hver um sig er rukkaður sem hér segir:

Örgjörvi: Xeon E5-2680 v4 @ 2.40GHz 64 þræðir.
Minni: 730 GB.
Java útgáfa: 1.8.0_111

Og hér er lykilatriðið magn gagna í töflunum sem þarf að lesa. Staðreyndin er sú að ef þú lest gögn úr töflu sem er að öllu leyti sett í HBase skyndiminni, þá kemur það ekki einu sinni til að lesa úr buff/skyndiminni stýrikerfisins. Vegna þess að HBase úthlutar sjálfgefið 40% af minni í uppbyggingu sem kallast BlockCache. Í meginatriðum er þetta ConcurrentHashMap, þar sem lykillinn er skráarnafn + offset á reitnum, og gildið er raunveruleg gögn við þessa frávik.

Svona, þegar aðeins er lesið úr þessari uppbyggingu, erum við við sjáum frábæran hraða, eins og milljón beiðnir á sekúndu. En við skulum ímynda okkur að við getum ekki úthlutað hundruðum gígabæta af minni bara fyrir gagnagrunnsþarfir, því það er fullt af öðrum gagnlegum hlutum í gangi á þessum netþjónum.

Til dæmis, í okkar tilviki, er rúmmál BlockCache á einum RS um 12 GB. Við lentum tveimur RS á einum hnút, þ.e. 96 GB er úthlutað fyrir BlockCache á öllum hnútum. Og það eru margfalt fleiri gögn, til dæmis, látum það vera 4 töflur, 130 svæði hver, þar sem skrár eru 800 MB að stærð, þjappaðar með FAST_DIFF, þ.e. samtals 410 GB (þetta eru hrein gögn, þ.e.a.s. án þess að taka tillit til afritunarþáttarins).

Þannig er BlockCache aðeins um 23% af heildargagnamagni og þetta er miklu nær raunverulegum aðstæðum í því sem kallast BigData. Og þetta er þar sem gamanið byrjar - því augljóslega, því færri skyndiminni smellir, því verri frammistaðan. Enda, ef þú missir af, þá þarftu að leggja mikið á þig - þ.e. farðu niður í hringikerfisaðgerðir. Hins vegar er ekki hægt að komast hjá þessu, svo við skulum líta á allt annan þátt - hvað verður um gögnin inni í skyndiminni?

Einfaldum aðstæður og gerum ráð fyrir að við höfum skyndiminni sem passar aðeins 1 hlut. Hér er dæmi um hvað mun gerast þegar við reynum að vinna með gagnamagn 3 sinnum stærra en skyndiminni, við verðum að:

1. Settu blokk 1 í skyndiminni
2. Fjarlægðu blokk 1 úr skyndiminni
3. Settu blokk 2 í skyndiminni
4. Fjarlægðu blokk 2 úr skyndiminni
5. Settu blokk 3 í skyndiminni

5 aðgerðum lokið! Hins vegar er ekki hægt að kalla þetta eðlilegt; í raun erum við að neyða HBase til að vinna fullt af algjörlega gagnslausu starfi. Það les stöðugt gögn úr skyndiminni stýrikerfisins, setur þau í BlockCache, aðeins til að henda þeim út nánast strax vegna þess að nýr hluti gagna er kominn. Hreyfimyndin í upphafi færslunnar sýnir kjarna vandans - Sorphirða fer úr mælikvarða, andrúmsloftið er að hitna, Greta litla í fjarlægu og heitu Svíþjóð er að verða í uppnámi. Og okkur upplýsingatæknifólki líkar það ekki þegar börn eru sorgmædd, svo við förum að hugsa um hvað við getum gert í því.

Hvað ef þú setur ekki allar blokkir í skyndiminni, heldur aðeins ákveðið hlutfall af þeim, svo skyndiminni flæði ekki yfir? Við skulum byrja á því að bæta aðeins nokkrum línum af kóða við upphaf aðgerðarinnar til að setja gögn í BlockCache:

  public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory) {
    if (cacheDataBlockPercent != 100 && buf.getBlockType().isData()) {
      if (cacheKey.getOffset() % 100 >= cacheDataBlockPercent) {
        return;
      }
    }
...

Aðalatriðið hér er eftirfarandi: offset er staðsetning kubbsins í skránni og síðustu tölustafir hans eru af handahófi og jafnt dreift frá 00 til 99. Þess vegna munum við aðeins sleppa þeim sem falla innan þess bils sem við þurfum.

Til dæmis, stilltu cacheDataBlockPercent = 20 og sjáðu hvað gerist:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Niðurstaðan er augljós. Í línuritunum hér að neðan verður ljóst hvers vegna slík hröðun átti sér stað - við spörum mikið af GC auðlindum án þess að gera Sisyfean vinnu við að setja gögn í skyndiminni aðeins til að henda þeim strax í holræsi Marshundanna:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Á sama tíma eykst CPU nýting, en er mun minni en framleiðni:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Það er líka athyglisvert að blokkirnar sem eru geymdar í BlockCache eru mismunandi. Flest, um 95%, eru gögnin sjálf. Og restin er lýsigögn, eins og Bloom síur eða LEAF_INDEX og Т.д.. Þessi gögn duga ekki, en þau eru mjög gagnleg, því áður en farið er beint í gögnin snýr HBase sér að meta til að skilja hvort nauðsynlegt sé að leita hér frekar og, ef svo er, hvar nákvæmlega áhugasviðið er staðsett.

Þess vegna sjáum við ávísunarskilyrði í kóðanum buf.getBlockType().isData() og þökk sé þessum meta munum við skilja það eftir í skyndiminni í öllum tilvikum.

Nú skulum við auka álagið og herða aðeins eiginleikann í einu lagi. Í fyrsta prófinu gerðum við niðurskurðarprósentuna = 20 og BlockCache var aðeins vannýtt. Nú skulum við stilla það á 23% og bæta við 100 þráðum á 5 mínútna fresti til að sjá á hvaða tímapunkti mettun á sér stað:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Hér sjáum við að upprunalega útgáfan nær nánast strax í loftið með um 100 þúsund beiðnum á sekúndu. En plásturinn gefur allt að 300 þúsund hröðun. Á sama tíma er ljóst að frekari hröðun er ekki lengur svo „ókeypis“; CPU-nýting eykst einnig.

Hins vegar er þetta ekki mjög glæsileg lausn, þar sem við vitum ekki fyrirfram hversu mörg prósent af blokkum þarf að vera í skyndiminni, það fer eftir hleðslusniði. Þess vegna var kerfi innleitt til að stilla þessa færibreytu sjálfkrafa eftir virkni lestraraðgerða.

Þremur valkostum hefur verið bætt við til að stjórna þessu:

hbase.lru.cache.heavy.viction.count — stillir hversu oft ferlið við að vísa gögnum úr skyndiminni ætti að keyra áður en við byrjum að nota hagræðingu (þ.e. að sleppa kubbum). Sjálfgefið er það jafnt og MAX_INT = 2147483647 og þýðir í raun að eiginleikinn mun aldrei byrja að vinna með þetta gildi. Vegna þess að brottflutningsferlið hefst á 5 - 10 sekúndna fresti (það fer eftir álagi) og 2147483647 * 10 / 60 / 60 / 24 / 365 = 680 ár. Hins vegar getum við stillt þessa færibreytu á 0 og látið eiginleikann virka strax eftir ræsingu.

Hins vegar er einnig farmur í þessari færibreytu. Ef álag okkar er þannig að skammtímalestrar (t.d. á daginn) og langtímalestrar (á nóttunni) eru stöðugt á milli, þá getum við tryggt að aðeins sé kveikt á eiginleikanum þegar langtímalestraraðgerðir eru í gangi.

Til dæmis vitum við að skammtímalestur varir venjulega um 1 mínútu. Það er engin þörf á að byrja að henda út kubbum, skyndiminni mun ekki hafa tíma til að verða úrelt og þá getum við stillt þessa færibreytu jafnt og td 10. Þetta mun leiða til þess að hagræðingin mun aðeins byrja að virka þegar langur- önn virkur lestur er hafinn, þ.e. á 100 sekúndum. Þannig, ef við höfum stuttan lestur, þá munu allar blokkir fara inn í skyndiminni og verða tiltækar (nema þeir sem verða gerðir út með venjulegu reikniritinu). Og þegar við gerum langtímalestur er kveikt á eiginleikanum og við myndum hafa mun meiri afköst.

hbase.lru.cache.heavy.eviction.mb.stærðartakmörk — stillir hversu mörg megabæti við viljum setja í skyndiminni (og, að sjálfsögðu, evict) á 10 sekúndum. Eiginleikinn mun reyna að ná þessu gildi og viðhalda því. Aðalatriðið er þetta: ef við ýtum gígabætum inn í skyndiminni, þá verðum við að útrýma gígabætum, og þetta, eins og við sáum hér að ofan, er mjög dýrt. Hins vegar ættir þú ekki að reyna að stilla það of lítið, þar sem þetta mun valda því að blokkaskiptingin hættir of snemma. Fyrir öfluga netþjóna (um 20-40 líkamlega kjarna) er best að stilla um 300-400 MB. Fyrir millistétt (~10 kjarna) 200-300 MB. Fyrir veik kerfi (2-5 kjarna) gæti 50-100 MB verið eðlilegt (ekki prófað á þessum).

Við skulum skoða hvernig þetta virkar: segjum að við setjum hbase.lru.cache.heavy.eviction.mb.size.limit = 500, það er einhvers konar álag (lestur) og síðan á ~10 sekúndna fresti reiknum við út hversu mörg bæti voru útskúfað með formúlunni:

Yfirkostnaður = Frelsuð bæti Summa (MB) * 100 / Takmörk (MB) - 100;

Ef í raun og veru 2000 MB voru rekin út, þá er kostnaður jafnt og:

2000 * 100 / 500 - 100 = 300%

Reikniritin reyna að viðhalda ekki meira en nokkrum tugum prósenta, þannig að eiginleikinn mun draga úr hlutfalli blokka í skyndiminni og innleiða þannig sjálfvirka stillingu.

Hins vegar, ef álagið minnkar, segjum að aðeins 200 MB sé hent út og Overhead verður neikvætt (svokallað yfirskot):

200 * 100 / 500 - 100 = -60%

Þvert á móti mun eiginleikinn auka hlutfall blokka í skyndiminni þar til Overhead verður jákvætt.

Hér að neðan er dæmi um hvernig þetta lítur út á raunverulegum gögnum. Það er engin þörf á að reyna að ná 0%, það er ómögulegt. Það er mjög gott þegar það er um 30 - 100%, þetta hjálpar til við að forðast ótímabært brotthvarf úr hagræðingarhamnum við skammtímabylgjur.

hbase.lru.cache.heavy.eviction.overhead stuðull — setur hversu fljótt við viljum fá niðurstöðuna. Ef við vitum með vissu að lestur okkar er að mestu leyti langur og viljum ekki bíða, getum við aukið þetta hlutfall og náð miklum afköstum hraðar.

Til dæmis setjum við þennan stuðul = 0.01. Þetta þýðir að kostnaður (sjá hér að ofan) verður margfaldaður með þessari tölu með niðurstöðunni og hlutfall blokka í skyndiminni mun minnka. Gerum ráð fyrir að Overhead = 300% og stuðull = 0.01, þá mun hlutfall blokka í skyndiminni minnka um 3%.

Svipuð „bakþrýstings“ rökfræði er einnig útfærð fyrir neikvæð kostnaðargildi (overshooting). Þar sem skammtímasveiflur í magni lestra og brottkasts eru alltaf mögulegar, gerir þetta kerfi þér kleift að forðast ótímabæra útgöngu úr fínstillingarhamnum. Bakþrýstingur hefur öfuga rökfræði: því sterkari sem yfirskotið er, því fleiri blokkir eru í skyndiminni.

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Framkvæmdarkóði

        LruBlockCache cache = this.cache.get();
        if (cache == null) {
          break;
        }
        freedSumMb += cache.evict()/1024/1024;
        /*
        * Sometimes we are reading more data than can fit into BlockCache
        * and it is the cause a high rate of evictions.
        * This in turn leads to heavy Garbage Collector works.
        * So a lot of blocks put into BlockCache but never read,
        * but spending a lot of CPU resources.
        * Here we will analyze how many bytes were freed and decide
        * decide whether the time has come to reduce amount of caching blocks.
        * It help avoid put too many blocks into BlockCache
        * when evict() works very active and save CPU for other jobs.
        * More delails: https://issues.apache.org/jira/browse/HBASE-23887
        */

        // First of all we have to control how much time
        // has passed since previuos evict() was launched
        // This is should be almost the same time (+/- 10s)
        // because we get comparable volumes of freed bytes each time.
        // 10s because this is default period to run evict() (see above this.wait)
        long stopTime = System.currentTimeMillis();
        if ((stopTime - startTime) > 1000 * 10 - 1) {
          // Here we have to calc what situation we have got.
          // We have the limit "hbase.lru.cache.heavy.eviction.bytes.size.limit"
          // and can calculte overhead on it.
          // We will use this information to decide,
          // how to change percent of caching blocks.
          freedDataOverheadPercent =
            (int) (freedSumMb * 100 / cache.heavyEvictionMbSizeLimit) - 100;
          if (freedSumMb > cache.heavyEvictionMbSizeLimit) {
            // Now we are in the situation when we are above the limit
            // But maybe we are going to ignore it because it will end quite soon
            heavyEvictionCount++;
            if (heavyEvictionCount > cache.heavyEvictionCountLimit) {
              // It is going for a long time and we have to reduce of caching
              // blocks now. So we calculate here how many blocks we want to skip.
              // It depends on:
             // 1. Overhead - if overhead is big we could more aggressive
              // reducing amount of caching blocks.
              // 2. How fast we want to get the result. If we know that our
              // heavy reading for a long time, we don't want to wait and can
              // increase the coefficient and get good performance quite soon.
              // But if we don't sure we can do it slowly and it could prevent
              // premature exit from this mode. So, when the coefficient is
              // higher we can get better performance when heavy reading is stable.
              // But when reading is changing we can adjust to it and set
              // the coefficient to lower value.
              int change =
                (int) (freedDataOverheadPercent * cache.heavyEvictionOverheadCoefficient);
              // But practice shows that 15% of reducing is quite enough.
              // We are not greedy (it could lead to premature exit).
              change = Math.min(15, change);
              change = Math.max(0, change); // I think it will never happen but check for sure
              // So this is the key point, here we are reducing % of caching blocks
              cache.cacheDataBlockPercent -= change;
              // If we go down too deep we have to stop here, 1% any way should be.
              cache.cacheDataBlockPercent = Math.max(1, cache.cacheDataBlockPercent);
            }
          } else {
            // Well, we have got overshooting.
            // Mayby it is just short-term fluctuation and we can stay in this mode.
            // It help avoid permature exit during short-term fluctuation.
            // If overshooting less than 90%, we will try to increase the percent of
            // caching blocks and hope it is enough.
            if (freedSumMb >= cache.heavyEvictionMbSizeLimit * 0.1) {
              // Simple logic: more overshooting - more caching blocks (backpressure)
              int change = (int) (-freedDataOverheadPercent * 0.1 + 1);
              cache.cacheDataBlockPercent += change;
              // But it can't be more then 100%, so check it.
              cache.cacheDataBlockPercent = Math.min(100, cache.cacheDataBlockPercent);
            } else {
              // Looks like heavy reading is over.
              // Just exit form this mode.
              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;
       }

Við skulum nú líta á þetta allt með því að nota raunverulegt dæmi. Við höfum eftirfarandi prófunarforskrift:

  1. Byrjum að skanna (25 þræðir, hópur = 100)
  2. Eftir 5 mínútur skaltu bæta við multi-getum (25 þræðir, lota = 100)
  3. Eftir 5 mínútur, slökktu á multi-gets (aðeins skönnun er eftir aftur)

Við gerum tvær keyrslur, fyrst hbase.lru.cache.heavy.eviction.count.limit = 10000 (sem gerir eiginleikann í raun óvirkur), og síðan stillum við takmörk = 0 (virkjar hann).

Í annálunum hér að neðan sjáum við hvernig kveikt er á eiginleikanum og endurstillir Overshooting í 14-71%. Af og til minnkar álagið, sem kveikir á bakþrýstingi og HBase vistar fleiri blokkir aftur.

Log RegionServer
evicted (MB): 0, hlutfall 0.0, kostnaður (%): -100, þungur eviction teller: 0, núverandi skyndiminni DataBlock (%): 100
evicted (MB): 0, hlutfall 0.0, kostnaður (%): -100, þungur eviction teller: 0, núverandi skyndiminni DataBlock (%): 100
evicted (MB): 2170, hlutfall 1.09, kostnaður (%): 985, þungur eviction teller: 1, núverandi skyndiminni DataBlock (%): 91 < byrjun
útskúfaður (MB): 3763, hlutfall 1.08, kostnaður (%): 1781, þungur brottreksturteljari: 2, núverandi skyndiminni DataBlock (%): 76
útskúfaður (MB): 3306, hlutfall 1.07, kostnaður (%): 1553, þungur brottreksturteljari: 3, núverandi skyndiminni DataBlock (%): 61
útskúfaður (MB): 2508, hlutfall 1.06, kostnaður (%): 1154, þungur brottreksturteljari: 4, núverandi skyndiminni DataBlock (%): 50
útskúfaður (MB): 1824, hlutfall 1.04, kostnaður (%): 812, þungur brottreksturteljari: 5, núverandi skyndiminni DataBlock (%): 42
útskúfaður (MB): 1482, hlutfall 1.03, kostnaður (%): 641, þungur brottreksturteljari: 6, núverandi skyndiminni DataBlock (%): 36
útskúfaður (MB): 1140, hlutfall 1.01, kostnaður (%): 470, þungur brottreksturteljari: 7, núverandi skyndiminni DataBlock (%): 32
útskúfaður (MB): 913, hlutfall 1.0, kostnaður (%): 356, þungur brottreksturteljari: 8, núverandi skyndiminni DataBlock (%): 29
útskúfaður (MB): 912, hlutfall 0.89, kostnaður (%): 356, þungur brottreksturteljari: 9, núverandi skyndiminni DataBlock (%): 26
útskúfaður (MB): 684, hlutfall 0.76, kostnaður (%): 242, þungur brottreksturteljari: 10, núverandi skyndiminni DataBlock (%): 24
útskúfaður (MB): 684, hlutfall 0.61, kostnaður (%): 242, þungur brottreksturteljari: 11, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 456, hlutfall 0.51, kostnaður (%): 128, þungur brottreksturteljari: 12, núverandi skyndiminni DataBlock (%): 21
útskúfaður (MB): 456, hlutfall 0.42, kostnaður (%): 128, þungur brottreksturteljari: 13, núverandi skyndiminni DataBlock (%): 20
útskúfaður (MB): 456, hlutfall 0.33, kostnaður (%): 128, þungur brottreksturteljari: 14, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 15, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 342, hlutfall 0.32, kostnaður (%): 71, þungur brottreksturteljari: 16, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 342, hlutfall 0.31, kostnaður (%): 71, þungur brottreksturteljari: 17, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.3, kostnaður (%): 14, þungur brottreksturteljari: 18, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.29, kostnaður (%): 14, þungur brottreksturteljari: 19, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.27, kostnaður (%): 14, þungur brottreksturteljari: 20, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.25, kostnaður (%): 14, þungur brottreksturteljari: 21, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.24, kostnaður (%): 14, þungur brottreksturteljari: 22, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.22, kostnaður (%): 14, þungur brottreksturteljari: 23, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.21, kostnaður (%): 14, þungur brottreksturteljari: 24, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.2, kostnaður (%): 14, þungur brottreksturteljari: 25, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 228, hlutfall 0.17, kostnaður (%): 14, þungur brottreksturteljari: 26, núverandi skyndiminni DataBlock (%): 19
evicted (MB): 456, hlutfall 0.17, kostnaður (%): 128, þungur eviction counter: 27, núverandi skyndiminni DataBlock (%): 18 < added gets (en tafla það sama)
útskúfaður (MB): 456, hlutfall 0.15, kostnaður (%): 128, þungur brottreksturteljari: 28, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 342, hlutfall 0.13, kostnaður (%): 71, þungur brottreksturteljari: 29, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 342, hlutfall 0.11, kostnaður (%): 71, þungur brottreksturteljari: 30, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 342, hlutfall 0.09, kostnaður (%): 71, þungur brottreksturteljari: 31, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 228, hlutfall 0.08, kostnaður (%): 14, þungur brottreksturteljari: 32, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 228, hlutfall 0.07, kostnaður (%): 14, þungur brottreksturteljari: 33, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 228, hlutfall 0.06, kostnaður (%): 14, þungur brottreksturteljari: 34, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 228, hlutfall 0.05, kostnaður (%): 14, þungur brottreksturteljari: 35, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 228, hlutfall 0.05, kostnaður (%): 14, þungur brottreksturteljari: 36, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 228, hlutfall 0.04, kostnaður (%): 14, þungur brottreksturteljari: 37, núverandi skyndiminni DataBlock (%): 17
evicted (MB): 109, hlutfall 0.04, kostnaður (%): -46, þungur eviction teller: 37, núverandi skyndiminni DataBlock (%): 22 < bakþrýstingur
útskúfaður (MB): 798, hlutfall 0.24, kostnaður (%): 299, þungur brottreksturteljari: 38, núverandi skyndiminni DataBlock (%): 20
útskúfaður (MB): 798, hlutfall 0.29, kostnaður (%): 299, þungur brottreksturteljari: 39, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 570, hlutfall 0.27, kostnaður (%): 185, þungur brottreksturteljari: 40, núverandi skyndiminni DataBlock (%): 17
útskúfaður (MB): 456, hlutfall 0.22, kostnaður (%): 128, þungur brottreksturteljari: 41, núverandi skyndiminni DataBlock (%): 16
útskúfaður (MB): 342, hlutfall 0.16, kostnaður (%): 71, þungur brottreksturteljari: 42, núverandi skyndiminni DataBlock (%): 16
útskúfaður (MB): 342, hlutfall 0.11, kostnaður (%): 71, þungur brottreksturteljari: 43, núverandi skyndiminni DataBlock (%): 16
útskúfaður (MB): 228, hlutfall 0.09, kostnaður (%): 14, þungur brottreksturteljari: 44, núverandi skyndiminni DataBlock (%): 16
útskúfaður (MB): 228, hlutfall 0.07, kostnaður (%): 14, þungur brottreksturteljari: 45, núverandi skyndiminni DataBlock (%): 16
útskúfaður (MB): 228, hlutfall 0.05, kostnaður (%): 14, þungur brottreksturteljari: 46, núverandi skyndiminni DataBlock (%): 16
útskúfaður (MB): 222, hlutfall 0.04, kostnaður (%): 11, þungur brottreksturteljari: 47, núverandi skyndiminni DataBlock (%): 16
evicted (MB): 104, hlutfall 0.03, kostnaður (%): -48, þungur eviction teller: 47, núverandi skyndiminni DataBlock (%): 21 < truflun fær
útskúfaður (MB): 684, hlutfall 0.2, kostnaður (%): 242, þungur brottreksturteljari: 48, núverandi skyndiminni DataBlock (%): 19
útskúfaður (MB): 570, hlutfall 0.23, kostnaður (%): 185, þungur brottreksturteljari: 49, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 342, hlutfall 0.22, kostnaður (%): 71, þungur brottreksturteljari: 50, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 228, hlutfall 0.21, kostnaður (%): 14, þungur brottreksturteljari: 51, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 228, hlutfall 0.2, kostnaður (%): 14, þungur brottreksturteljari: 52, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 228, hlutfall 0.18, kostnaður (%): 14, þungur brottreksturteljari: 53, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 228, hlutfall 0.16, kostnaður (%): 14, þungur brottreksturteljari: 54, núverandi skyndiminni DataBlock (%): 18
útskúfaður (MB): 228, hlutfall 0.14, kostnaður (%): 14, þungur brottreksturteljari: 55, núverandi skyndiminni DataBlock (%): 18
evicted (MB): 112, hlutfall 0.14, kostnaður (%): -44, þungur eviction teller: 55, núverandi skyndiminni DataBlock (%): 23 < bakþrýstingur
útskúfaður (MB): 456, hlutfall 0.26, kostnaður (%): 128, þungur brottreksturteljari: 56, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.31, kostnaður (%): 71, þungur brottreksturteljari: 57, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 58, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 59, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 60, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 61, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 62, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 63, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.32, kostnaður (%): 71, þungur brottreksturteljari: 64, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 65, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 66, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.32, kostnaður (%): 71, þungur brottreksturteljari: 67, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 68, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.32, kostnaður (%): 71, þungur brottreksturteljari: 69, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.32, kostnaður (%): 71, þungur brottreksturteljari: 70, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 71, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 72, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 73, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 74, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 75, núverandi skyndiminni DataBlock (%): 22
útskúfaður (MB): 342, hlutfall 0.33, kostnaður (%): 71, þungur brottreksturteljari: 76, núverandi skyndiminni DataBlock (%): 22
evicted (MB): 21, hlutfall 0.33, kostnaður (%): -90, þungur eviction teller: 76, núverandi skyndiminni DataBlock (%): 32
evicted (MB): 0, hlutfall 0.0, kostnaður (%): -100, þungur eviction teller: 0, núverandi skyndiminni DataBlock (%): 100
evicted (MB): 0, hlutfall 0.0, kostnaður (%): -100, þungur eviction teller: 0, núverandi skyndiminni DataBlock (%): 100

Skannanir voru nauðsynlegar til að sýna sama ferlið í formi línurits um sambandið milli tveggja skyndiminnihluta - stakra (þar sem blokkir sem aldrei hefur verið beðið um áður) og margra (gögn „beðið um“ að minnsta kosti einu sinni eru geymd hér):

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Og að lokum, hvernig lítur virkni breytanna út í formi línurits. Til samanburðar var algjörlega slökkt á skyndiminni í upphafi, síðan var HBase ræst með skyndiminni og seinkun á byrjun hagræðingarvinnu um 5 mínútur (30 eviction cycles).

Fullan kóða má finna í Pull Request HBASE 23887 á github.

Hins vegar eru 300 þúsund lestur á sekúndu ekki allt sem hægt er að ná á þessum vélbúnaði við þessar aðstæður. Staðreyndin er sú að þegar þú þarft að fá aðgang að gögnum í gegnum HDFS er ShortCircuitCache (hér eftir nefnt SSC) vélbúnaðurinn notaður, sem gerir þér kleift að nálgast gögnin beint og forðast netsamskipti.

Sniðgreining sýndi að þó þessi vélbúnaður gefi mikinn ávinning, þá verður hann líka á einhverjum tímapunkti flöskuháls, því næstum allar þungar aðgerðir eiga sér stað inni í læsingu, sem leiðir til lokunar oftast.

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Eftir að hafa áttað okkur á þessu komumst við að því að hægt er að sniðganga vandamálið með því að búa til fjölda óháðra SSC:

private final ShortCircuitCache[] shortCircuitCache;
...
shortCircuitCache = new ShortCircuitCache[this.clientShortCircuitNum];
for (int i = 0; i < this.clientShortCircuitNum; i++)
  this.shortCircuitCache[i] = new ShortCircuitCache(…);

Og vinnið síðan með þau, að gatnamótum undanskildum líka á síðasta númeri frávikið:

public ShortCircuitCache getShortCircuitCache(long idx) {
    return shortCircuitCache[(int) (idx % clientShortCircuitNum)];
}

Nú geturðu byrjað að prófa. Til að gera þetta munum við lesa skrár frá HDFS með einföldu fjölþráða forriti. Stilltu breytur:

conf.set("dfs.client.read.shortcircuit", "true");
conf.set("dfs.client.read.shortcircuit.buffer.size", "65536"); // по дефолту = 1 МБ и это сильно замедляет чтение, поэтому лучше привести в соответствие к реальным нуждам
conf.set("dfs.client.short.circuit.num", num); // от 1 до 10

Og lestu bara skrárnar:

FSDataInputStream in = fileSystem.open(path);
for (int i = 0; i < count; i++) {
    position += 65536;
    if (position > 900000000)
        position = 0L;
    int res = in.read(position, byteBuffer, 0, 65536);
}

Þessi kóði er keyrður í aðskildum þræði og við munum fjölga samtímis lesnum skrám (frá 10 til 200 - láréttur ás) og fjölda skyndiminni (frá 1 í 10 - grafík). Lóðrétti ásinn sýnir hröðunina sem stafar af aukningu á SSC miðað við tilvikið þegar það er aðeins eitt skyndiminni.

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Hvernig á að lesa grafið: Framkvæmdartími fyrir 100 þúsund lestur í 64 KB kubbum með einu skyndiminni þarf 78 sekúndur. Með 5 skyndiminni tekur það 16 sekúndur. Þeir. það er ~5 sinnum hröðun. Eins og sést á línuritinu eru áhrifin ekki mjög áberandi fyrir lítinn fjölda samhliða lestra, þau byrja að gegna áberandi hlutverki þegar þráðalestur eru yfir 50. Einnig er áberandi að fjölga SSC úr 6 og að ofan gefur verulega minni frammistöðuaukningu.

Athugasemd 1: þar sem prófunarniðurstöðurnar eru nokkuð sveiflukenndar (sjá hér að neðan), voru gerðar 3 keyrslur og meðaltalsgildin sem fengust.

Athugasemd 2: Ávinningurinn af því að stilla handahófskenndan aðgang er sá sami, þó að aðgangurinn sjálfur sé aðeins hægari.

Hins vegar er nauðsynlegt að skýra að, ólíkt tilfellinu með HBase, er þessi hröðun ekki alltaf ókeypis. Hér „opnum“ við getu CPU til að vinna meira, í stað þess að hanga á læsingum.

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Hér getur þú séð að almennt, aukning á fjölda skyndiminni gefur um það bil hlutfallslega aukningu á CPU nýtingu. Hins vegar eru aðeins fleiri vinningssamsetningar.

Til dæmis skulum við skoða nánar stillinguna SSC = 3. Frammistöðuaukningin á sviðinu er um 3.3 sinnum. Hér að neðan eru niðurstöður úr öllum þremur aðskildum hlaupunum.

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Þó CPU neysla eykst um það bil 2.8 sinnum. Munurinn er ekki mikill en Greta litla er nú þegar ánægð og hefur kannski tíma til að mæta í skólann og læra.

Þannig mun þetta hafa jákvæð áhrif fyrir öll tæki sem nota magnaðgang að HDFS (til dæmis Spark, o.s.frv.), að því tilskildu að forritskóðinn sé léttur (þ.e.a.s. innstungan er á HDFS biðlarahlið) og það er ókeypis örgjörvaafl. . Til að athuga, skulum prófa hvaða áhrif sameinuð notkun BlockCache fínstillingar og SSC stillingar fyrir lestur frá HBase mun hafa.

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Það má sjá að við slíkar aðstæður eru áhrifin ekki eins mikil og í fáguðum prófum (lestur án nokkurrar vinnslu), en það er alveg hægt að kreista út 80K til viðbótar hér. Saman veita báðar fínstillingarnar allt að 4x hraða.

Einnig var gerð PR fyrir þessa hagræðingu [HDFS-15202], sem hefur verið sameinað og þessi virkni verður fáanleg í framtíðarútgáfum.

Og að lokum var áhugavert að bera saman lestrarframmistöðu svipaðs gagnagrunns með breiðum dálkum, Cassandra og HBase.

Til að gera þetta settum við af stað tilvik af venjulegu YCSB hleðsluprófunartæki frá tveimur gestgjöfum (alls 800 þræðir). Á miðlarahliðinni - 4 tilvik af RegionServer og Cassandra á 4 vélum (ekki þeim þar sem viðskiptavinirnir eru í gangi, til að forðast áhrif þeirra). Lestrar komu úr töflum af stærð:

HBase – 300 GB á HDFS (100 GB hrein gögn)

Cassandra - 250 GB (afritunarstuðull = 3)

Þeir. rúmmálið var um það bil það sama (í HBase aðeins meira).

HBase færibreytur:

dfs.client.short.circuit.num = 5 (HDFS viðskiptavinur fínstilling)

hbase.lru.cache.heavy.eviction.count.limit = 30 - þetta þýðir að plásturinn mun byrja að virka eftir 30 brottrekstur (~5 mínútur)

hbase.lru.cache.heavy.eviction.mb.size.limit = 300 — markrúmmál skyndiminni og brottvísunar

YCSB annálar voru flokkaðar og settar saman í Excel línurit:

Hvernig á að auka leshraða frá HBase allt að 3 sinnum og frá HDFS allt að 5 sinnum

Eins og þú sérð gera þessar hagræðingar það mögulegt að bera saman árangur þessara gagnagrunna við þessar aðstæður og ná 450 þúsund lestum á sekúndu.

Við vonum að þessar upplýsingar geti verið gagnlegar fyrir einhvern í spennandi baráttu fyrir framleiðni.

Heimild: www.habr.com

Bæta við athugasemd