Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Didelis našumas yra vienas iš pagrindinių reikalavimų dirbant su dideliais duomenimis. „Sberbank“ duomenų įkėlimo skyriuje beveik visas operacijas perkeliame į „Hadoop“ pagrįstą duomenų debesį ir todėl susiduriame su tikrai dideliais informacijos srautais. Natūralu, kad mes visada ieškome būdų, kaip pagerinti našumą, o dabar norime papasakoti, kaip mums pavyko pataisyti RegionServer HBase ir HDFS klientą, kurio dėka galėjome žymiai padidinti skaitymo operacijų greitį.
Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Tačiau prieš pereinant prie patobulinimų esmės, verta pakalbėti apie apribojimus, kurių iš esmės negalima apeiti, jei sėdite ant HDD.

Kodėl HDD ir greitas atsitiktinės prieigos skaitymas yra nesuderinami
Kaip žinote, HBase ir daugelis kitų duomenų bazių saugo duomenis kelių dešimčių kilobaitų dydžio blokuose. Pagal numatytuosius nustatymus jis yra apie 64 KB. Dabar įsivaizduokime, kad mums reikia gauti tik 100 baitų, ir paprašome HBase pateikti šiuos duomenis naudodami tam tikrą raktą. Kadangi HFiles bloko dydis yra 64 KB, užklausa bus 640 kartų didesnė (tik minutę!) nei reikia.

Toliau, nes užklausa bus pateikta per HDFS ir jos metaduomenų talpyklos mechanizmą ShortCircuitCache (tai leidžia tiesiogiai pasiekti failus), tai nuskaito jau 1 MB iš disko. Tačiau tai galima reguliuoti naudojant parametrą dfs.client.read.shortcircuit.buffer.size ir daugeliu atvejų prasminga šią reikšmę sumažinti, pavyzdžiui, iki 126 KB.

Tarkime, tai darome, bet be to, kai pradedame skaityti duomenis per java API, pvz., tokias funkcijas kaip FileChannel.read ir paprašome operacinės sistemos nuskaityti nurodytą duomenų kiekį, ji nuskaito „tik tuo atveju“ 2 kartus daugiau , t.y. 256 KB mūsų atveju. Taip yra todėl, kad java nėra lengvas būdas nustatyti FADV_RANDOM vėliavėlę, kad būtų išvengta tokio elgesio.

Dėl to, norint gauti 100 baitų, po gaubtu nuskaitoma 2600 kartų daugiau. Atrodytų, sprendimas akivaizdus, ​​sumažinkime bloko dydį iki kilobaito, uždėkime minėtą vėliavėlę ir įgaukime didelį nušvitimo pagreitį. Bet bėda ta, kad sumažinę bloko dydį 2 kartus, 2 kartus sumažiname ir perskaitomų baitų skaičių per laiko vienetą.

Galima gauti tam tikrą naudą nustačius vėliavėlę FADV_RANDOM, tačiau tik naudojant aukštą daugiagiją ir 128 KB bloko dydį, tačiau tai yra daugiausia kelios dešimtys procentų:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Bandymai buvo atlikti su 100 failų, kurių kiekvienas yra 1 GB dydžio ir yra 10 HDD.

Apskaičiuokime, ko iš esmės galime tikėtis tokiu greičiu:
Tarkime, skaitome iš 10 diskų 280 MB/sek greičiu, t.y. 3 milijonai kartų 100 baitų. Tačiau, kaip prisimename, mums reikia 2600 kartų mažiau duomenų nei nuskaitoma. Taigi 3 milijonus padaliname iš 2600 ir gauname 1100 įrašų per sekundę.

Slegia, ar ne? Tokia gamta Atsitiktinė prieiga prieiga prie duomenų HDD – nepriklausomai nuo bloko dydžio. Tai yra fizinė atsitiktinės prieigos riba ir tokiomis sąlygomis jokia duomenų bazė negali išspausti daugiau.

Kaip tada duomenų bazės pasiekia daug didesnį greitį? Norėdami atsakyti į šį klausimą, pažiūrėkime, kas vyksta toliau pateiktame paveikslėlyje:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Čia matome, kad pirmąsias kelias minutes greitis tikrai siekia apie tūkstantį rekordų per sekundę. Tačiau toliau, dėl to, kad nuskaitoma daug daugiau, nei buvo prašoma, duomenys patenka į operacinės sistemos (Linux) buff/cache ir greitis padidėja iki padoresnių 60 tūkst per sekundę.

Taigi, toliau nagrinėsime paspartintą prieigą tik prie duomenų, kurie yra OS talpykloje arba yra panašios prieigos greičio SSD/NVMe saugojimo įrenginiuose.

Mūsų atveju testus atliksime 4 serverių stende, kurių kiekvienas apmokestinamas taip:

CPU: Xeon E5-2680 v4 @ 2.40 GHz 64 gijos.
Atmintis: 730 GB.
java versija: 1.8.0_111

Ir čia svarbiausia yra duomenų kiekis lentelėse, kurį reikia perskaityti. Faktas yra tas, kad jei skaitote duomenis iš lentelės, kuri visiškai yra HBase talpykloje, tada jie net nebus nuskaitomi iš operacinės sistemos bufeto / talpyklos. Kadangi HBase pagal numatytuosius nustatymus skiria 40% atminties struktūrai, vadinamai BlockCache. Iš esmės tai yra ConcurrentHashMap, kur raktas yra failo pavadinimas + bloko poslinkis, o reikšmė yra faktiniai šio poslinkio duomenys.

Taigi, skaitydami tik iš šios struktūros, mes matome puikų greitį, pavyzdžiui, milijonas užklausų per sekundę. Tačiau įsivaizduokime, kad negalime skirti šimtų gigabaitų atminties vien duomenų bazių poreikiams, nes šiuose serveriuose veikia daugybė kitų naudingų dalykų.

Pavyzdžiui, mūsų atveju „BlockCache“ talpa viename RS yra apie 12 GB. Ant vieno mazgo nusileidome du RS, t.y. 96 GB yra skirta „BlockCache“ visuose mazguose. O duomenų yra daug kartų daugiau, pavyzdžiui, tebūnie 4 lentelės po 130 regionų, kuriose failai yra 800 MB dydžio, suspausti FAST_DIFF, t.y. viso 410 GB (tai gryni duomenys, t.y. neatsižvelgiant į replikacijos koeficientą).

Taigi „BlockCache“ sudaro tik apie 23% viso duomenų kiekio ir tai yra daug artimesnė tikrosioms „BigData“ sąlygoms. Ir čia prasideda linksmybės – nes akivaizdu, kad kuo mažiau talpyklos pataikymų, tuo prastesnis našumas. Juk nespėjus teks daug dirbti – t.y. pereikite prie iškvietimo sistemos funkcijų. Tačiau to išvengti nepavyks, tad pažvelkime visiškai kitu aspektu – kas atsitiks su duomenimis talpyklos viduje?

Supaprastinkime situaciją ir tarkime, kad turime talpyklą, kuri telpa tik 1 objektui. Štai pavyzdys, kas nutiks, kai bandysime dirbti su 3 kartus didesniu duomenų kiekiu nei talpykla, turėsime:

1. Į talpyklą įdėkite 1 bloką
2. Pašalinkite 1 bloką iš talpyklos
3. Į talpyklą įdėkite 2 bloką
4. Pašalinkite 2 bloką iš talpyklos
5. Į talpyklą įdėkite 3 bloką

5 veiksmai atlikti! Tačiau ši situacija negali būti vadinama normalia, iš tikrųjų mes verčiame HBase atlikti daugybę visiškai nenaudingų darbų. Jis nuolat nuskaito duomenis iš OS talpyklos, įdeda juos į BlockCache, bet beveik iš karto išmeta, nes atkeliavo nauja duomenų dalis. Animacija įrašo pradžioje parodo problemos esmę – Šiukšlių surinkėjas išeina iš mastelio, atmosfera kaista, mažoji Greta tolimoje ir karštoje Švedijoje ima nervintis. O mes, IT žmonės, labai nemėgstame, kai vaikai liūdi, todėl pradedame galvoti, ką galime dėl to padaryti.

Ką daryti, jei talpykloje įdėsite ne visus blokus, o tik tam tikrą procentą jų, kad talpykla neperpildytų? Pradėkime tiesiog pridėkite kelias kodo eilutes prie funkcijos, skirtos duomenims į BlockCache, pradžioje:

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

Esmė yra tokia: poslinkis yra bloko padėtis faile, o paskutiniai jo skaitmenys yra atsitiktinai ir tolygiai paskirstyti nuo 00 iki 99. Todėl praleisime tik tuos, kurie patenka į mums reikalingą diapazoną.

Pavyzdžiui, nustatykite cacheDataBlockPercent = 20 ir pažiūrėkite, kas atsitiks:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Rezultatas akivaizdus. Žemiau pateiktose diagramose tampa aišku, kodėl įvyko toks pagreitis – sutaupome daug GC resursų, neatlikdami sizifiško darbo – talpindami duomenis į talpyklą, kad iš karto išmestume juos į Marso šunų kanalizaciją:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Tuo pačiu metu procesoriaus panaudojimas didėja, bet yra daug mažesnis nei našumas:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Taip pat verta paminėti, kad „BlockCache“ saugomi blokai yra skirtingi. Dauguma, apie 95%, yra patys duomenys. O visa kita yra metaduomenys, pvz., „Bloom“ filtrai arba LEAF_INDEX ir т.д.. Šių duomenų nepakanka, bet jie labai naudingi, nes prieš tiesiogiai prieidama prie duomenų HBase atsigręžia į meta, kad suprastų, ar reikia čia ieškoti toliau ir, jei taip, kur tiksliai yra dominantis blokas.

Todėl kode matome patikrinimo sąlygą buf.getBlockType().isData() ir šios meta dėka bet kokiu atveju paliksime ją talpykloje.

Dabar vienu ypu padidinkime apkrovą ir šiek tiek sustiprinkime funkciją. Pirmajame bandyme padarėme ribinį procentą = 20, o „BlockCache“ buvo šiek tiek nepakankamai išnaudota. Dabar nustatykime jį į 23% ir pridėkite 100 gijų kas 5 minutes, kad pamatytume, kuriuo momentu atsiranda sodrumas:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Čia matome, kad originali versija beveik iš karto pasiekia lubas apie 100 tūkstančių užklausų per sekundę. Tuo tarpu pleistras suteikia pagreitį iki 300 tūkst. Kartu akivaizdu, kad tolesnis pagreitis nebėra toks „nemokamas“, didėja ir procesoriaus panaudojimas.

Tačiau tai nėra labai elegantiškas sprendimas, nes mes iš anksto nežinome, kiek procentų blokų reikia talpinti, tai priklauso nuo apkrovos profilio. Todėl buvo įdiegtas mechanizmas, leidžiantis automatiškai koreguoti šį parametrą priklausomai nuo skaitymo operacijų aktyvumo.

Pridėtos trys parinktys tai valdyti:

hbase.lru.cache.heavy.eviction.count.limit — nustato, kiek kartų duomenų iškeldinimo iš talpyklos procesas turi vykti prieš pradedant naudoti optimizavimą (t. y. praleidžiant blokus). Pagal numatytuosius nustatymus jis yra lygus MAX_INT = 2147483647 ir iš tikrųjų reiškia, kad funkcija niekada nepradės veikti su šia reikšme. Kadangi iškeldinimo procesas prasideda kas 5 - 10 sekundžių (tai priklauso nuo apkrovos) ir 2147483647 * 10 / 60 / 60 / 24 / 365 = 680 metų. Tačiau galime nustatyti šį parametrą į 0 ir priversti funkciją veikti iškart po paleidimo.

Tačiau šiame parametre taip pat yra naudingoji apkrova. Jei mūsų apkrova yra tokia, kad trumpalaikiai skaitymai (tarkim dieną) ir ilgalaikiai skaitymai (naktį) yra nuolat įsiterpę, tai galime įsitikinti, kad funkcija įjungiama tik tada, kai atliekamos ilgos skaitymo operacijos.

Pavyzdžiui, žinome, kad trumpalaikiai skaitymai paprastai trunka apie 1 minutę. Nereikia pradėti mesti blokų, talpykla nespės pasenti ir tada galime nustatyti šį parametrą lygų, pavyzdžiui, 10. Tai lems, kad optimizavimas pradės veikti tik tada, kai ilgai prasidėjęs terminas aktyvus skaitymas, t.y. per 100 sekundžių. Taigi, jei turėsime trumpalaikį skaitymą, visi blokai pateks į talpyklą ir bus pasiekiami (išskyrus tuos, kurie bus iškeldinti pagal standartinį algoritmą). O kai atliekame ilgalaikį skaitymą, funkcija įjungiama ir turėtume daug didesnį našumą.

hbase.lru.cache.heavy.eviction.mb.size.limit — nustato, kiek megabaitų norėtume įdėti į talpyklą (ir, žinoma, iškeldinti) per 10 sekundžių. Funkcija bandys pasiekti šią vertę ir ją išlaikyti. Esmė tokia: jei į talpyklą įterpsime gigabaitus, turėsime iškeldinti gigabaitus, o tai, kaip matėme aukščiau, yra labai brangu. Tačiau neturėtumėte bandyti jo nustatyti per mažo, nes dėl to bloko praleidimo režimas bus išjungtas anksčiau laiko. Galingiems serveriams (apie 20-40 fizinių branduolių) optimalu nustatyti apie 300-400 MB. Vidurinei klasei (~10 branduolių) 200-300 MB. Silpnose sistemose (2–5 branduoliai) 50–100 MB gali būti normalu (jose neišbandyta).

Pažiūrėkime, kaip tai veikia: tarkime, nustatėme hbase.lru.cache.heavy.eviction.mb.size.limit = 500, yra kažkoks apkrovimas (skaitymas) ir tada kas ~10 sekundžių skaičiuojame, kiek baitų buvo iškeldinta naudojant formulę:

Pridėtinė vertė = atlaisvintų baitų suma (MB) * 100 / riba (MB) - 100;

Jei iš tikrųjų buvo iškeldinta 2000 MB, tada pridėtinės išlaidos yra lygios:

2000 * 100 / 500 - 100 = 300 %

Algoritmai stengiasi išlaikyti ne daugiau kaip kelias dešimtis procentų, todėl funkcija sumažins talpykloje esančių blokų procentą ir taip įdiegs automatinio derinimo mechanizmą.

Tačiau, jei apkrova nukrenta, tarkime, kad iškeldinama tik 200 MB, o Overhead tampa neigiamas (vadinamasis viršijimas):

200 * 100 / 500 - 100 = -60 %

Priešingai, ši funkcija padidins talpykloje esančių blokų procentą, kol Overhead taps teigiamas.

Toliau pateikiamas pavyzdys, kaip tai atrodo realiais duomenimis. Nereikia stengtis pasiekti 0%, tai neįmanoma. Labai gerai, kai jis yra apie 30 - 100%, tai padeda išvengti ankstyvo išėjimo iš optimizavimo režimo trumpalaikių viršįtampių metu.

hbase.lru.cache.heavy.eviction.overhead.coefficient — nustato, kaip greitai norėtume gauti rezultatą. Jei tikrai žinome, kad mūsų skaitymai dažniausiai būna ilgi ir nenorime laukti, galime padidinti šį santykį ir greičiau pasiekti aukštą našumą.

Pavyzdžiui, nustatome šį koeficientą = 0.01. Tai reiškia, kad pridėtinės išlaidos (žr. aukščiau) bus padaugintos iš šio skaičiaus iš gauto rezultato ir bus sumažintas talpykloje esančių blokų procentas. Tarkime, kad Overhead = 300%, o koeficientas = 0.01, tada talpykloje esančių blokų procentas bus sumažintas 3%.

Panaši „atgalinio slėgio“ logika taip pat įgyvendinama esant neigiamoms papildomoms (viršijančioms) vertėms. Kadangi visada galimi trumpalaikiai skaitymų ir iškeldinimo apimties svyravimai, šis mechanizmas leidžia išvengti ankstyvo išėjimo iš optimizavimo režimo. Atgalinis slėgis turi atvirkštinę logiką: kuo stipresnis viršijimas, tuo daugiau blokų yra talpykloje.

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Diegimo kodas

        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;
       }

Dabar pažvelkime į visa tai naudodamiesi tikru pavyzdžiu. Turime tokį bandymo scenarijų:

  1. Pradėkime nuskaityti (25 gijos, paketas = 100)
  2. Po 5 minučių pridėkite kelis elementus (25 gijos, partija = 100)
  3. Po 5 minučių išjunkite multi-gets (vėl lieka tik nuskaitymas)

Atliekame du paleidimus: pirmiausia hbase.lru.cache.heavy.eviction.count.limit = 10000 (tai iš tikrųjų išjungia funkciją), tada nustatome limitą = 0 (įjungia).

Žemiau esančiuose žurnaluose matome, kaip ši funkcija įjungta, ir iš naujo nustato viršijimą į 14–71%. Kartkartėmis apkrova mažėja, o tai įjungia Backpressure ir HBase vėl talpina daugiau blokų.

Prisijungti RegionServer
iškeldinta (MB): 0, santykis 0.0, pridėtinės išlaidos (%): -100, didelis iškeldinimo skaitiklis: 0, dabartinis talpyklos duomenų blokas (%): 100
iškeldinta (MB): 0, santykis 0.0, pridėtinės išlaidos (%): -100, didelis iškeldinimo skaitiklis: 0, dabartinis talpyklos duomenų blokas (%): 100
iškeldinta (MB): 2170, santykis 1.09, pridėtinės išlaidos (%): 985, didelis iškeldinimo skaitiklis: 1, dabartinis talpyklos duomenų blokas (%): 91 < pradžia
iškeldinta (MB): 3763, santykis 1.08, pridėtinės išlaidos (%): 1781, didelis iškeldinimo skaitiklis: 2, dabartinis talpyklos duomenų blokas (%): 76
iškeldinta (MB): 3306, santykis 1.07, pridėtinės išlaidos (%): 1553, didelis iškeldinimo skaitiklis: 3, dabartinis talpyklos duomenų blokas (%): 61
iškeldinta (MB): 2508, santykis 1.06, pridėtinės išlaidos (%): 1154, didelis iškeldinimo skaitiklis: 4, dabartinis talpyklos duomenų blokas (%): 50
iškeldinta (MB): 1824, santykis 1.04, pridėtinės išlaidos (%): 812, didelis iškeldinimo skaitiklis: 5, dabartinis talpyklos duomenų blokas (%): 42
iškeldinta (MB): 1482, santykis 1.03, pridėtinės išlaidos (%): 641, didelis iškeldinimo skaitiklis: 6, dabartinis talpyklos duomenų blokas (%): 36
iškeldinta (MB): 1140, santykis 1.01, pridėtinės išlaidos (%): 470, didelis iškeldinimo skaitiklis: 7, dabartinis talpyklos duomenų blokas (%): 32
iškeldinta (MB): 913, santykis 1.0, pridėtinės išlaidos (%): 356, didelis iškeldinimo skaitiklis: 8, dabartinis talpyklos duomenų blokas (%): 29
iškeldinta (MB): 912, santykis 0.89, pridėtinės išlaidos (%): 356, didelis iškeldinimo skaitiklis: 9, dabartinis talpyklos duomenų blokas (%): 26
iškeldinta (MB): 684, santykis 0.76, pridėtinės išlaidos (%): 242, didelis iškeldinimo skaitiklis: 10, dabartinis talpyklos duomenų blokas (%): 24
iškeldinta (MB): 684, santykis 0.61, pridėtinės išlaidos (%): 242, didelis iškeldinimo skaitiklis: 11, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 456, santykis 0.51, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 12, dabartinis talpyklos duomenų blokas (%): 21
iškeldinta (MB): 456, santykis 0.42, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 13, dabartinis talpyklos duomenų blokas (%): 20
iškeldinta (MB): 456, santykis 0.33, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 14, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 15, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 342, santykis 0.32, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 16, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 342, santykis 0.31, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 17, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.3, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 18, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.29, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 19, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.27, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 20, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.25, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 21, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.24, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 22, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.22, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 23, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.21, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 24, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.2, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 25, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 228, santykis 0.17, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 26, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 456, santykis 0.17, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 27, dabartinis talpyklos duomenų blokas (%): 18 < pridėta gauna (bet lentelė ta pati)
iškeldinta (MB): 456, santykis 0.15, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 28, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 342, santykis 0.13, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 29, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 342, santykis 0.11, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 30, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 342, santykis 0.09, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 31, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 228, santykis 0.08, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 32, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 228, santykis 0.07, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 33, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 228, santykis 0.06, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 34, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 228, santykis 0.05, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 35, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 228, santykis 0.05, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 36, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 228, santykis 0.04, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 37, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 109, santykis 0.04, pridėtinės išlaidos (%): -46, didelis iškeldinimo skaitiklis: 37, dabartinis talpyklos duomenų blokas (%): 22 < priešslėgis
iškeldinta (MB): 798, santykis 0.24, pridėtinės išlaidos (%): 299, didelis iškeldinimo skaitiklis: 38, dabartinis talpyklos duomenų blokas (%): 20
iškeldinta (MB): 798, santykis 0.29, pridėtinės išlaidos (%): 299, didelis iškeldinimo skaitiklis: 39, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 570, santykis 0.27, pridėtinės išlaidos (%): 185, didelis iškeldinimo skaitiklis: 40, dabartinis talpyklos duomenų blokas (%): 17
iškeldinta (MB): 456, santykis 0.22, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 41, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 342, santykis 0.16, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 42, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 342, santykis 0.11, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 43, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 228, santykis 0.09, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 44, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 228, santykis 0.07, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 45, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 228, santykis 0.05, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 46, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 222, santykis 0.04, pridėtinės išlaidos (%): 11, didelis iškeldinimo skaitiklis: 47, dabartinis talpyklos duomenų blokas (%): 16
iškeldinta (MB): 104, santykis 0.03, pridėtinės išlaidos (%): -48, didelis iškeldinimo skaitiklis: 47, dabartinis talpyklos duomenų blokas (%): 21 < pertraukti gauna
iškeldinta (MB): 684, santykis 0.2, pridėtinės išlaidos (%): 242, didelis iškeldinimo skaitiklis: 48, dabartinis talpyklos duomenų blokas (%): 19
iškeldinta (MB): 570, santykis 0.23, pridėtinės išlaidos (%): 185, didelis iškeldinimo skaitiklis: 49, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 342, santykis 0.22, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 50, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 228, santykis 0.21, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 51, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 228, santykis 0.2, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 52, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 228, santykis 0.18, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 53, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 228, santykis 0.16, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 54, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 228, santykis 0.14, pridėtinės išlaidos (%): 14, didelis iškeldinimo skaitiklis: 55, dabartinis talpyklos duomenų blokas (%): 18
iškeldinta (MB): 112, santykis 0.14, pridėtinės išlaidos (%): -44, didelis iškeldinimo skaitiklis: 55, dabartinis talpyklos duomenų blokas (%): 23 < priešslėgis
iškeldinta (MB): 456, santykis 0.26, pridėtinės išlaidos (%): 128, didelis iškeldinimo skaitiklis: 56, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.31, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 57, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 58, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 59, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 60, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 61, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 62, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 63, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.32, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 64, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 65, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 66, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.32, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 67, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 68, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.32, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 69, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.32, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 70, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 71, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 72, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 73, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 74, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 75, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 342, santykis 0.33, pridėtinės išlaidos (%): 71, didelis iškeldinimo skaitiklis: 76, dabartinis talpyklos duomenų blokas (%): 22
iškeldinta (MB): 21, santykis 0.33, pridėtinės išlaidos (%): -90, didelis iškeldinimo skaitiklis: 76, dabartinis talpyklos duomenų blokas (%): 32
iškeldinta (MB): 0, santykis 0.0, pridėtinės išlaidos (%): -100, didelis iškeldinimo skaitiklis: 0, dabartinis talpyklos duomenų blokas (%): 100
iškeldinta (MB): 0, santykis 0.0, pridėtinės išlaidos (%): -100, didelis iškeldinimo skaitiklis: 0, dabartinis talpyklos duomenų blokas (%): 100

Nuskaitymai buvo reikalingi norint parodyti tą patį procesą dviejų talpyklos sekcijų santykio diagramos pavidalu - vieno (kur anksčiau niekada nebuvo prašoma blokų) ir kelių (čia saugomi bent kartą „prašomi“ duomenys):

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Ir galiausiai, kaip atrodo parametrų veikimas grafiko pavidalu. Palyginimui, pradžioje buvo visiškai išjungta talpykla, vėliau paleista HBase su talpyklomis ir optimizavimo darbų pradžią atidėjus 5 minutėmis (30 iškeldinimo ciklų).

Visą kodą galite rasti ištraukimo užklausoje HBASE 23887 github'e.

Tačiau 300 tūkstančių skaitymų per sekundę nėra viskas, ką galima pasiekti naudojant šią aparatinę įrangą tokiomis sąlygomis. Faktas yra tas, kad kai jums reikia pasiekti duomenis per HDFS, naudojamas „ShortCircuitCache“ (toliau – SSC) mechanizmas, leidžiantis tiesiogiai pasiekti duomenis, išvengiant tinklo sąveikos.

Profiliavimas parodė, kad nors šis mechanizmas duoda didelį naudą, bet tam tikru momentu jis tampa ir kliūtimi, nes beveik visos sunkios operacijos atliekamos spynos viduje, todėl dažniausiai blokuojama.

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Tai supratę, supratome, kad problemą galima apeiti sukuriant nepriklausomų SSC masyvą:

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

Ir tada dirbkite su jais, neįskaitant sankryžų ir prie paskutinio poslinkio skaitmens:

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

Dabar galite pradėti testavimą. Norėdami tai padaryti, perskaitysime failus iš HDFS naudodami paprastą kelių gijų programą. Nustatykite parametrus:

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

Ir tiesiog perskaitykite failus:

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);
}

Šis kodas vykdomas atskiromis gijomis ir padidinsime vienu metu skaitomų failų skaičių (nuo 10 iki 200 – horizontalioji ašis) ir talpyklų skaičių (nuo 1 iki 10 – grafika). Vertikali ašis rodo pagreitį, atsirandantį dėl SSC padidėjimo, palyginti su tuo atveju, kai yra tik viena talpykla.

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Kaip skaityti grafiką: 100 tūkstančių nuskaitymų 64 KB blokuose su viena talpykla vykdymo laikas reikalauja 78 sekundžių. Tuo tarpu su 5 talpyklomis tai užtrunka 16 sekundžių. Tie. yra ~5 kartų pagreitis. Kaip matyti iš grafiko, nedideliam lygiagrečių nuskaitymų skaičiui efektas nėra labai pastebimas, jis pradeda vaidinti pastebimą vaidmenį, kai gijų skaitymų yra daugiau nei 50. Taip pat pastebima, kad SSC skaičius padidinamas nuo 6 ir aukščiau suteikia žymiai mažesnį našumo padidėjimą.

1 pastaba: kadangi bandymo rezultatai yra gana nepastovūs (žr. toliau), buvo atlikti 3 bandymai ir gautų verčių vidurkis.

2 pastaba: našumo padidėjimas sukonfigūravus atsitiktinę prieigą yra toks pat, nors pati prieiga yra šiek tiek lėtesnė.

Tačiau būtina paaiškinti, kad, skirtingai nei HBase atveju, šis pagreitis ne visada yra nemokamas. Čia mes „atrakiname“ procesoriaus galimybę dirbti daugiau, o ne kabėti ant spynų.

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Čia galite pastebėti, kad apskritai padidėjus talpyklų skaičiui, apytiksliai proporcingai padidėja procesoriaus panaudojimas. Tačiau yra šiek tiek daugiau laiminčių derinių.

Pavyzdžiui, atidžiau pažvelkime į nustatymą SSC = 3. Veikimo padidėjimas diapazone yra apie 3.3 karto. Žemiau pateikiami visų trijų atskirų važiavimų rezultatai.

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Tuo tarpu procesoriaus suvartojimas padidėja maždaug 2.8 karto. Skirtumas nėra labai didelis, bet mažoji Greta jau laiminga ir gali turėti laiko lankyti mokyklą bei pamokas.

Taigi tai turės teigiamą poveikį bet kuriam įrankiui, kuris naudoja masinę prieigą prie HDFS (pvz., Spark ir kt.), jei programos kodas yra lengvas (t. y. kištukas yra HDFS kliento pusėje) ir yra laisvos procesoriaus energijos. . Norėdami patikrinti, išbandykite, kokį poveikį turės bendras BlockCache optimizavimo ir SSC derinimas skaitymui iš HBase.

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Matyti, kad tokiomis sąlygomis efektas nėra toks didelis, kaip rafinuotuose testuose (skaitant be jokio apdorojimo), bet čia visiškai įmanoma išspausti papildomus 80K. Kartu abu optimizavimai užtikrina iki 4 kartų pagreitį.

Šiam optimizavimui taip pat buvo sukurtas PR [HDFS-15202], kuris buvo sujungtas ir ši funkcija bus pasiekiama būsimuose leidimuose.

Ir galiausiai buvo įdomu palyginti panašios plačių stulpelių duomenų bazės „Cassandra“ ir „HBase“ skaitymo našumą.

Norėdami tai padaryti, paleidome standartinės YCSB apkrovos testavimo priemonės egzempliorius iš dviejų pagrindinių kompiuterių (iš viso 800 gijų). Serverio pusėje – 4 RegionServer ir Cassandra egzemplioriai 4 pagrindiniuose kompiuteriuose (ne tuose, kuriuose veikia klientai, kad būtų išvengta jų įtakos). Rodmenys buvo gauti iš lentelių dydžio:

HBase – 300 GB HDFS (100 GB grynų duomenų)

„Cassandra“ – 250 GB (replikacijos koeficientas = 3)

Tie. tūris buvo maždaug toks pat (HBase šiek tiek daugiau).

HBase parametrai:

dfs.client.short.circuit.num = 5 (HDFS kliento optimizavimas)

hbase.lru.cache.heavy.eviction.count.limit = 30 - tai reiškia, kad pleistras pradės veikti po 30 iškeldinimo (~5 min.)

hbase.lru.cache.heavy.eviction.mb.size.limit = 300 — tikslinė talpyklos ir iškeldinimo apimtis

YCSB žurnalai buvo išanalizuoti ir sudaryti į „Excel“ grafikus:

Kaip padidinti skaitymo greitį iš HBase iki 3 kartų ir iš HDFS iki 5 kartų

Kaip matote, šie optimizavimai leidžia palyginti šių duomenų bazių našumą tokiomis sąlygomis ir pasiekti 450 tūkst. skaitymų per sekundę.

Tikimės, kad ši informacija bus kam nors naudinga jaudinančios kovos dėl produktyvumo metu.

Šaltinis: www.habr.com

Добавить комментарий