Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Hege prestaasjes is ien fan 'e wichtichste easken by it wurkjen mei grutte data. Yn 'e ôfdieling gegevensladen by Sberbank pompe wy hast alle transaksjes yn ús Hadoop-basearre Data Cloud en dogge dêrom mei echt grutte streamen fan ynformaasje. Natuerlik sykje wy altyd nei manieren om prestaasjes te ferbetterjen, en no wolle wy jo fertelle hoe't wy it slagge hawwe om RegionServer HBase en de HDFS-kliïnt te patchjen, wêrtroch't wy de snelheid fan lêsoperaasjes signifikant kinne ferheegje.
Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Foardat jo lykwols oergeane nei de essinsje fan 'e ferbetteringen, is it wurdich te praten oer beheiningen dy't yn prinsipe net kinne wurde omseame as jo op in HDD sitte.

Wêrom HDD en snelle Random Access-lêzen binne ynkompatibel
Lykas jo witte, bewarje HBase, en in protte oare databases, gegevens yn blokken fan ferskate tsientallen kilobytes yn grutte. Standert giet it oer 64 KB. Litte wy ús no foarstelle dat wy mar 100 bytes moatte krije en wy freegje HBase om ús dizze gegevens te jaan mei in bepaalde kaai. Sûnt de blokgrutte yn HFiles is 64 KB, sil it fersyk 640 kear grutter wêze (mar in minút!) As nedich.

Folgjende, sûnt it fersyk sil gean troch HDFS en syn metadata caching meganisme ShortCircuitCache (wat jout direkte tagong ta triemmen), dit liedt ta it lêzen fan al 1 MB fan de skiif. Dit kin lykwols oanpast wurde mei de parameter dfs.client.read.shortcircuit.buffer.grutte en yn in protte gefallen makket it sin om dizze wearde te ferminderjen, bygelyks nei 126 KB.

Litte wy sizze dat wy dit dogge, mar boppedat, as wy begjinne mei it lêzen fan gegevens fia de java-api, lykas funksjes lykas FileChannel.read en it bestjoeringssysteem freegje om de opjûne hoemannichte gegevens te lêzen, lêst it "foar it gefal" 2 kear mear , d.w.s. 256 KB yn ús gefal. Dit komt om't java gjin maklike manier hat om de FADV_RANDOM-flagge yn te stellen om dit gedrach te foarkommen.

As gefolch, om ús 100 bytes te krijen, wurdt 2600 kear mear ûnder de motorkap lêzen. It soe lykje dat de oplossing is fanselssprekkend, lit ús ferminderje it blok grutte ta in kilobyte, set de neamde flagge en fa grutte ferljochting fersnelling. Mar it probleem is dat troch it ferminderjen fan de blokgrutte troch 2 kear, wy ek ferminderje it oantal bytes lêzen per ienheid fan tiid troch 2 kear.

Guon winst fan it ynstellen fan de FADV_RANDOM flagge kin wurde krigen, mar allinich mei hege multi-threading en mei in blokgrutte fan 128 KB, mar dit is in maksimum fan in pear tsientallen prosint:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Tests waarden útfierd op 100 triemmen, elk 1 GB yn grutte en leit op 10 HDDs.

Litte wy berekkenje wêr't wy yn prinsipe op rekkenje kinne op dizze snelheid:
Litte wy sizze dat wy lêze fan 10 skiven mei in snelheid fan 280 MB / sek, d.w.s. 3 miljoen kear 100 bytes. Mar sa't wy ûnthâlde, binne de gegevens dy't wy nedich binne 2600 kear minder as wat wurdt lêzen. Sa diele wy 3 miljoen troch 2600 en krije 1100 records per sekonde.

Depressyf, is it net? Dat is de natuer Willekeurige tagong tagong ta gegevens op 'e HDD - nettsjinsteande de blokgrutte. Dit is de fysike limyt fan willekeurige tagong en gjin databank kin ûnder sokke betingsten mear útdrukke.

Hoe berikke databases dan folle hegere snelheden? Om dizze fraach te beantwurdzjen, litte wy sjen nei wat der bart yn 'e folgjende ôfbylding:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Hjir sjogge wy dat foar de earste pear minuten de snelheid echt sa'n tûzen records per sekonde is. Troch it feit dat der lykwols folle mear lêzen wurdt dan oanfrege, komme de gegevens lykwols yn 'e buff/cache fan it bestjoeringssysteem (linux) en nimt de snelheid ta in fatsoenliker 60 tûzen per sekonde

Sa sille wy fierder omgean mei it fersnellen fan tagong allinich foar de gegevens dy't yn 'e OS-cache binne of lizze yn SSD / NVMe-opslachapparaten fan fergelykbere tagongssnelheid.

Yn ús gefal sille wy tests útfiere op in bankje fan 4 servers, elk dêrfan wurdt as folget belêste:

CPU: Xeon E5-2680 v4 @ 2.40GHz 64 triedden.
Unthâld: 730 GB.
java ferzje: 1.8.0_111

En hjir is it kaaipunt de hoemannichte gegevens yn 'e tabellen dy't lêzen wurde moat. It feit is dat as jo gegevens lêze fan in tabel dy't folslein yn 'e HBase-cache pleatst is, dan sil it net iens komme om te lêzen fan' e buff / cache fan it bestjoeringssysteem. Omdat HBase standert allocates 40% fan ûnthâld oan in struktuer neamd BlockCache. Yn essinsje is dit in ConcurrentHashMap, wêrby't de kaai de triemnamme + offset fan it blok is, en de wearde is de eigentlike gegevens by dizze offset.

Sa, by it lêzen fan allinnich út dizze struktuer, wy wy sjogge poerbêst snelheid, lykas in miljoen fersiken per sekonde. Mar litte wy ús foarstelle dat wy hûnderten gigabytes oan ûnthâld net allinich kinne tawize foar databankbehoeften, om't d'r in protte oare nuttige dingen op dizze servers rinne.

Bygelyks, yn ús gefal is it folume fan BlockCache op ien RS sawat 12 GB. Wy lâne twa RS op ien knooppunt, d.w.s. 96 GB wurde tawiisd foar BlockCache op alle knopen. En der is in protte kearen mear gegevens, bygelyks, lit it wêze 4 tabellen, 130 regio elk, wêryn triemmen binne 800 MB yn grutte, komprimearre troch FAST_DIFF, i.e. in totaal fan 410 GB (dit is suvere gegevens, d.w.s. sûnder rekken hâldend mei de replikaasje faktor).

Sa is BlockCache mar sawat 23% fan it totale datavolumint en dit is folle tichter by de echte betingsten fan wat BigData hjit. En dit is wêr't de wille begjint - want fansels, hoe minder cache hits, hoe minder de prestaasjes. As jo ​​​​ommers misse, sille jo in protte wurk moatte dwaan - d.w.s. gean del nei oprop systeem funksjes. Dit kin lykwols net foarkommen wurde, dus litte wy nei in folslein oar aspekt sjen - wat bart der mei de gegevens yn 'e cache?

Litte wy de situaasje ferienfâldigje en oannimme dat wy in cache hawwe dy't allinich past by 1 objekt. Hjir is in foarbyld fan wat der barre sil as wy besykje te wurkjen mei in gegevensvolumint 3 kear grutter dan de cache, wy sille moatte:

1. Plak blok 1 yn cache
2. Fuortsmite blok 1 út cache
3. Plak blok 2 yn cache
4. Fuortsmite blok 2 út cache
5. Plak blok 3 yn cache

5 aksjes foltôge! Dizze situaasje kin lykwols net normaal wurde neamd; yn feite twinge wy HBase om in boskje folslein nutteloos wurk te dwaan. It lêst konstant gegevens fan 'e OS-cache, pleatst it yn BlockCache, allinich om it hast fuortendaliks út te smiten, om't in nij diel fan gegevens is oankaam. De animaasje oan it begjin fan 'e post toant de essinsje fan it probleem - Garbage Collector giet fan skaal ôf, de sfear wurdt ferwaarme, lytse Greta yn it fiere en waarme Sweden wurdt oerstjoer. En wy IT-minsken hâlde der echt net fan as bern fertrietlik binne, dus wy begjinne te tinken oer wat wy der oan kinne dwaan.

Wat as jo net alle blokken yn 'e cache pleatse, mar allinich in bepaald persintaazje dêrfan, sadat de cache net oerstreamt? Litte wy begjinne troch gewoan in pear rigels koade ta te foegjen oan it begjin fan 'e funksje foar it pleatsen fan gegevens yn BlockCache:

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

It punt hjir is it folgjende: offset is de posysje fan it blok yn 'e triem en syn lêste sifers wurde willekeurich en lykwichtich ferdield fan 00 nei 99. Dêrom sille wy allinne oerslaan dyjingen dy't falle yn it berik dat wy nedich.

Stel bygelyks cacheDataBlockPercent = 20 yn en sjoch wat der bart:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

It resultaat is dúdlik. Yn 'e grafiken hjirûnder wurdt it dúdlik wêrom't sa'n fersnelling barde - wy besparje in protte GC-boarnen sûnder it Sisyphean wurk te dwaan om gegevens yn 'e cache te pleatsen, allinich om it fuortendaliks yn' e drain fan 'e Mars-hûnen te smiten:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Tagelyk nimt CPU-gebrûk ta, mar is folle minder dan produktiviteit:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

It is ek de muoite wurdich op te merken dat de blokken opslein yn BlockCache ferskillend binne. De measte, sawat 95%, binne gegevens sels. En de rest is metadata, lykas Bloom filters of LEAF_INDEX en ensfh.. Dizze gegevens binne net genôch, mar it is heul nuttich, om't foardat jo direkt tagong krije ta de gegevens, draait HBase nei de meta om te begripen oft it nedich is om hjir fierder te sykjen en, as dat sa is, wêr't krekt it blok fan belang is.

Dêrom sjogge wy yn 'e koade in kontrôlebetingst buf.getBlockType().isData() en tank oan dizze meta, wy litte it yn alle gefallen yn 'e cache.

Litte wy no de lading ferheegje en de funksje yn ien kear in bytsje oanskerpe. Yn 'e earste test makken wy it besunigingspersintaazje = 20 en BlockCache waard in bytsje underutilized. Litte wy it no ynstelle op 23% en elke 100 minuten 5 triedden tafoegje om te sjen op hokker punt sêding foarkomt:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Hjir sjogge wy dat de orizjinele ferzje hast fuortendaliks it plafond op sa'n 100 tûzen oanfragen per sekonde rekket. Wylst de patch jout in fersnelling fan oant 300 tûzen. Tagelyk is it dúdlik dat fierdere fersnelling net mear sa "fergees" is; CPU-gebrûk nimt ek ta.

Dit is lykwols net in heul elegante oplossing, om't wy net fan tefoaren witte hoefolle persintaazje blokken moatte wurde cache, it hinget ôf fan it loadprofyl. Dêrom waard in meganisme ynfierd om dizze parameter automatysk oan te passen ôfhinklik fan 'e aktiviteit fan lêsoperaasjes.

Trije opsjes binne tafoege om dit te kontrolearjen:

hbase.lru.cache.heavy.eviction.count.limit - stelt yn hoefolle kearen it proses fan it ferwiderjen fan gegevens út 'e cache moat rinne foardat wy begjinne mei it brûken fan optimisaasje (dus blokken oerslaan). Standert is it gelyk oan MAX_INT = 2147483647 en betsjut feitlik dat de funksje nea sil begjinne te wurkjen mei dizze wearde. Omdat de eviction proses begjint elke 5 - 10 sekonden (it hinget ôf fan de lading) en 2147483647 * 10 / 60 / 60 / 24 / 365 = 680 jier. Wy kinne dizze parameter lykwols op 0 ynstelle en de funksje direkt nei lansearring meitsje.

D'r is lykwols ek in lading yn dizze parameter. As ús lading sa is dat koarte termyn lêzings (sizze oerdeis) en lange termyn lêzings (by nacht) konstant wurde ôfwiksele, dan kinne wy ​​derfoar soargje dat de funksje allinich ynskeakele is as lange lêsoperaasjes oan 'e gong binne.

Bygelyks, wy witte dat koarte-termyn lêzingen meastal duorret likernôch 1 minút. D'r is net nedich om blokken út te smiten, de cache sil gjin tiid hawwe om ferâldere te wurden en dan kinne wy ​​dizze parameter gelyk ynstelle oan bygelyks 10. Dit sil liede ta it feit dat de optimalisaasje allinich sil begjinne te wurkjen as lang- term aktyf lêzen is begûn, d.w.s. yn 100 sekonden. Dus, as wy in koarte termyn lêze hawwe, dan sille alle blokken yn 'e cache gean en sille beskikber wêze (útsein dejingen dy't troch it standertalgoritme wurde útstutsen). En as wy lêze op lange termyn dogge, wurdt de funksje ynskeakele en soene wy ​​folle hegere prestaasjes hawwe.

hbase.lru.cache.heavy.eviction.mb.size.limit - stelt yn hoefolle megabytes wy wolle pleatse yn 'e cache (en, fansels, evict) yn 10 sekonden. De funksje sil besykje dizze wearde te berikken en te behâlden. It punt is dit: as wy gigabytes yn 'e cache skodzje, dan moatte wy gigabytes útsette, en dit, lykas wy hjirboppe seagen, is heul djoer. Jo moatte lykwols net besykje it te lyts yn te stellen, om't dit de blok-skip-modus te betiid sil ôfslute. Foar krêftige tsjinners (sawat 20-40 fysike kearnen) is it optimaal om sa'n 300-400 MB yn te stellen. Foar de middenklasse (~ 10 kearnen) 200-300 MB. Foar swakke systemen (2-5 kearnen) kin 50-100 MB normaal wêze (net hifke op dizze).

Litte wy sjen hoe't dit wurket: lit ús sizze dat wy hbase.lru.cache.heavy.eviction.mb.size.limit = 500 ynstelle, d'r is in soarte fan lading (lêzen) en dan elke ~ 10 sekonden berekkenje wy hoefolle bytes wiene útset mei de formule:

Overhead = Freed Bytes Sum (MB) * 100 / Limyt (MB) - 100;

As yn feite 2000 MB waarden útstutsen, dan is Overhead gelyk oan:

2000 * 100 / 500 - 100 = 300%

De algoritmen besykje te behâlden net mear as in pear tsientallen prosint, sadat de funksje sil ferminderje it persintaazje fan cached blokken, dêrmei it útfieren fan in auto-tuning meganisme.

As de lading lykwols sakket, litte wy sizze dat mar 200 MB wurde útstutsen en Overhead wurdt negatyf (de saneamde overshooting):

200 * 100 / 500 - 100 = -60%

Krektoarsom, de funksje sil it persintaazje cache blokken ferheegje oant Overhead posityf wurdt.

Hjirûnder is in foarbyld fan hoe't dit sjocht op echte gegevens. It is net nedich om te besykjen om 0% te berikken, it is ûnmooglik. It is heul goed as it oer 30 - 100% giet, dit helpt om foartiid út 'e optimisaasjemodus te foarkommen tidens koarte termyn surges.

hbase.lru.cache.heavy.eviction.overhead.coefficient - stelt yn hoe fluch wy it resultaat wolle krije. As wy wis witte dat ús lêzingen meast lang binne en net wolle wachtsje, kinne wy ​​dizze ferhâlding ferheegje en rapper hege prestaasjes krije.

Bygelyks, wy sette dizze koeffizient = 0.01. Dit betsjut dat Overhead (sjoch hjirboppe) wurdt fermannichfâldige mei dit getal troch it resultearjende resultaat en it persintaazje cached blokken wurdt fermindere. Litte wy oannimme dat Overhead = 300% en koëffisjint = 0.01, dan sil it persintaazje cached blokken wurde fermindere mei 3%.

In soartgelikense "Backpressure" logika wurdt ek ymplemintearre foar negative Overhead (overshooting) wearden. Sûnt koarte-termyn fluktuaasjes yn it folume fan lêzen en evictions binne altyd mooglik, dit meganisme kinne jo foarkomme te betiid útgong fan de optimalisaasje modus. Backpressure hat in omkearde logika: hoe sterker de overshooting, hoe mear blokken wurde yn it cache bewarre.

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Implementaasje koade

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

Litte wy no dit alles besjen mei in echt foarbyld. Wy hawwe it folgjende testskript:

  1. Litte wy begjinne te dwaan Scan (25 threads, batch = 100)
  2. Foegje nei 5 minuten multi-gets ta (25 threads, batch = 100)
  3. Nei 5 minuten, skeakelje multi-gets út (allinich scan bliuwt wer)

Wy dogge twa runs, earst hbase.lru.cache.heavy.eviction.count.limit = 10000 (dy't eins útskeakelje de funksje), en dan set limyt = 0 (moogt it).

Yn 'e logs hjirûnder sjogge wy hoe't de funksje ynskeakele is en Overshooting weromsette nei 14-71%. Fan tiid ta tiid nimt de lading ôf, wêrtroch Backpressure ynskeakele wurdt en HBase wer mear blokken caches.

Log RegionServer
evicted (MB): 0, ferhâlding 0.0, overhead (%): -100, swiere eviction counter: 0, aktuele caching DataBlock (%): 100
evicted (MB): 0, ferhâlding 0.0, overhead (%): -100, swiere eviction counter: 0, aktuele caching DataBlock (%): 100
útset (MB): 2170, ferhâlding 1.09, overhead (%): 985, swiere útsettingteller: 1, aktuele caching DataBlock (%): 91 < begjin
evicted (MB): 3763, ferhâlding 1.08, overhead (%): 1781, swiere eviction counter: 2, aktuele caching DataBlock (%): 76
evicted (MB): 3306, ferhâlding 1.07, overhead (%): 1553, swiere eviction counter: 3, aktuele caching DataBlock (%): 61
evicted (MB): 2508, ferhâlding 1.06, overhead (%): 1154, swiere eviction counter: 4, aktuele caching DataBlock (%): 50
evicted (MB): 1824, ferhâlding 1.04, overhead (%): 812, swiere eviction counter: 5, aktuele caching DataBlock (%): 42
evicted (MB): 1482, ferhâlding 1.03, overhead (%): 641, swiere eviction counter: 6, aktuele caching DataBlock (%): 36
evicted (MB): 1140, ferhâlding 1.01, overhead (%): 470, swiere eviction counter: 7, aktuele caching DataBlock (%): 32
evicted (MB): 913, ferhâlding 1.0, overhead (%): 356, swiere eviction counter: 8, aktuele caching DataBlock (%): 29
evicted (MB): 912, ferhâlding 0.89, overhead (%): 356, swiere eviction counter: 9, aktuele caching DataBlock (%): 26
evicted (MB): 684, ferhâlding 0.76, overhead (%): 242, swiere eviction counter: 10, aktuele caching DataBlock (%): 24
evicted (MB): 684, ferhâlding 0.61, overhead (%): 242, swiere eviction counter: 11, aktuele caching DataBlock (%): 22
evicted (MB): 456, ferhâlding 0.51, overhead (%): 128, swiere eviction counter: 12, aktuele caching DataBlock (%): 21
evicted (MB): 456, ferhâlding 0.42, overhead (%): 128, swiere eviction counter: 13, aktuele caching DataBlock (%): 20
evicted (MB): 456, ferhâlding 0.33, overhead (%): 128, swiere eviction counter: 14, aktuele caching DataBlock (%): 19
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 15, aktuele caching DataBlock (%): 19
evicted (MB): 342, ferhâlding 0.32, overhead (%): 71, swiere eviction counter: 16, aktuele caching DataBlock (%): 19
evicted (MB): 342, ferhâlding 0.31, overhead (%): 71, swiere eviction counter: 17, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.3, overhead (%): 14, swiere eviction counter: 18, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.29, overhead (%): 14, swiere eviction counter: 19, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.27, overhead (%): 14, swiere eviction counter: 20, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.25, overhead (%): 14, swiere eviction counter: 21, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.24, overhead (%): 14, swiere eviction counter: 22, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.22, overhead (%): 14, swiere eviction counter: 23, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.21, overhead (%): 14, swiere eviction counter: 24, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.2, overhead (%): 14, swiere eviction counter: 25, aktuele caching DataBlock (%): 19
evicted (MB): 228, ferhâlding 0.17, overhead (%): 14, swiere eviction counter: 26, aktuele caching DataBlock (%): 19
evicted (MB): 456, ferhâlding 0.17, overhead (%): 128, swiere eviction counter: 27, aktuele caching DataBlock (%): 18 < tafoege krijt (mar tabel itselde)
evicted (MB): 456, ferhâlding 0.15, overhead (%): 128, swiere eviction counter: 28, aktuele caching DataBlock (%): 17
evicted (MB): 342, ferhâlding 0.13, overhead (%): 71, swiere eviction counter: 29, aktuele caching DataBlock (%): 17
evicted (MB): 342, ferhâlding 0.11, overhead (%): 71, swiere eviction counter: 30, aktuele caching DataBlock (%): 17
evicted (MB): 342, ferhâlding 0.09, overhead (%): 71, swiere eviction counter: 31, aktuele caching DataBlock (%): 17
evicted (MB): 228, ferhâlding 0.08, overhead (%): 14, swiere eviction counter: 32, aktuele caching DataBlock (%): 17
evicted (MB): 228, ferhâlding 0.07, overhead (%): 14, swiere eviction counter: 33, aktuele caching DataBlock (%): 17
evicted (MB): 228, ferhâlding 0.06, overhead (%): 14, swiere eviction counter: 34, aktuele caching DataBlock (%): 17
evicted (MB): 228, ferhâlding 0.05, overhead (%): 14, swiere eviction counter: 35, aktuele caching DataBlock (%): 17
evicted (MB): 228, ferhâlding 0.05, overhead (%): 14, swiere eviction counter: 36, aktuele caching DataBlock (%): 17
evicted (MB): 228, ferhâlding 0.04, overhead (%): 14, swiere eviction counter: 37, aktuele caching DataBlock (%): 17
evicted (MB): 109, ferhâlding 0.04, overhead (%): -46, swiere eviction counter: 37, aktuele caching DataBlock (%): 22 < efterdruk
evicted (MB): 798, ferhâlding 0.24, overhead (%): 299, swiere eviction counter: 38, aktuele caching DataBlock (%): 20
evicted (MB): 798, ferhâlding 0.29, overhead (%): 299, swiere eviction counter: 39, aktuele caching DataBlock (%): 18
evicted (MB): 570, ferhâlding 0.27, overhead (%): 185, swiere eviction counter: 40, aktuele caching DataBlock (%): 17
evicted (MB): 456, ferhâlding 0.22, overhead (%): 128, swiere eviction counter: 41, aktuele caching DataBlock (%): 16
evicted (MB): 342, ferhâlding 0.16, overhead (%): 71, swiere eviction counter: 42, aktuele caching DataBlock (%): 16
evicted (MB): 342, ferhâlding 0.11, overhead (%): 71, swiere eviction counter: 43, aktuele caching DataBlock (%): 16
evicted (MB): 228, ferhâlding 0.09, overhead (%): 14, swiere eviction counter: 44, aktuele caching DataBlock (%): 16
evicted (MB): 228, ferhâlding 0.07, overhead (%): 14, swiere eviction counter: 45, aktuele caching DataBlock (%): 16
evicted (MB): 228, ferhâlding 0.05, overhead (%): 14, swiere eviction counter: 46, aktuele caching DataBlock (%): 16
evicted (MB): 222, ferhâlding 0.04, overhead (%): 11, swiere eviction counter: 47, aktuele caching DataBlock (%): 16
evicted (MB): 104, ferhâlding 0.03, overhead (%): -48, swiere eviction counter: 47, aktuele caching DataBlock (%): 21 < ûnderbrekking krijt
evicted (MB): 684, ferhâlding 0.2, overhead (%): 242, swiere eviction counter: 48, aktuele caching DataBlock (%): 19
evicted (MB): 570, ferhâlding 0.23, overhead (%): 185, swiere eviction counter: 49, aktuele caching DataBlock (%): 18
evicted (MB): 342, ferhâlding 0.22, overhead (%): 71, swiere eviction counter: 50, aktuele caching DataBlock (%): 18
evicted (MB): 228, ferhâlding 0.21, overhead (%): 14, swiere eviction counter: 51, aktuele caching DataBlock (%): 18
evicted (MB): 228, ferhâlding 0.2, overhead (%): 14, swiere eviction counter: 52, aktuele caching DataBlock (%): 18
evicted (MB): 228, ferhâlding 0.18, overhead (%): 14, swiere eviction counter: 53, aktuele caching DataBlock (%): 18
evicted (MB): 228, ferhâlding 0.16, overhead (%): 14, swiere eviction counter: 54, aktuele caching DataBlock (%): 18
evicted (MB): 228, ferhâlding 0.14, overhead (%): 14, swiere eviction counter: 55, aktuele caching DataBlock (%): 18
evicted (MB): 112, ferhâlding 0.14, overhead (%): -44, swiere eviction counter: 55, aktuele caching DataBlock (%): 23 < efterdruk
evicted (MB): 456, ferhâlding 0.26, overhead (%): 128, swiere eviction counter: 56, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.31, overhead (%): 71, swiere eviction counter: 57, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 58, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 59, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 60, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 61, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 62, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 63, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.32, overhead (%): 71, swiere eviction counter: 64, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 65, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 66, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.32, overhead (%): 71, swiere eviction counter: 67, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 68, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.32, overhead (%): 71, swiere eviction counter: 69, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.32, overhead (%): 71, swiere eviction counter: 70, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 71, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 72, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 73, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 74, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 75, aktuele caching DataBlock (%): 22
evicted (MB): 342, ferhâlding 0.33, overhead (%): 71, swiere eviction counter: 76, aktuele caching DataBlock (%): 22
evicted (MB): 21, ferhâlding 0.33, overhead (%): -90, swiere eviction counter: 76, aktuele caching DataBlock (%): 32
evicted (MB): 0, ferhâlding 0.0, overhead (%): -100, swiere eviction counter: 0, aktuele caching DataBlock (%): 100
evicted (MB): 0, ferhâlding 0.0, overhead (%): -100, swiere eviction counter: 0, aktuele caching DataBlock (%): 100

De scans wiene nedich om itselde proses sjen te litten yn 'e foarm fan in grafyk fan' e relaasje tusken twa cache-seksjes - single (wêr't blokken dy't noch nea earder oanfrege binne) en multi (gegevens "oanfrege" op syn minst ien kear wurde hjir opslein):

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

En as lêste, hoe sjocht de operaasje fan 'e parameters út yn' e foarm fan in grafyk. Foar fergeliking waard de cache oan it begjin folslein útskeakele, doe waard HBase lansearre mei caching en it begjin fan optimisaasjewurk fertrage mei 5 minuten (30 eviction cycles).

Folsleine koade is te finen yn Pull Request HBASE 23887 op github.

Lykwols, 300 tûzen lêzen per sekonde is net alles dat kin wurde berikt op dizze hardware ûnder dizze betingsten. It feit is dat as jo tagong hawwe ta gegevens fia HDFS, wurdt de ShortCircuitCache (hjirnei oantsjutten as SSC) meganisme brûkt, wêrtroch jo direkt tagong krije kinne ta de gegevens, om netwurk ynteraksjes te foarkommen.

Profilearring die bliken dat hoewol't dit meganisme jout in grutte winst, it ek op in stuit wurdt in knelpunt, omdat hast alle swiere operaasjes plakfine binnen in slot, dat liedt ta blokkearjen meastentiids.

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Nei't wy dit realisearre hawwe, realisearren wy dat it probleem kin wurde omseame troch in array fan ûnôfhinklike SSC's te meitsjen:

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

En wurkje dan mei har, útsein krusingen ek by de lêste offset sifer:

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

No kinne jo begjinne te testen. Om dit te dwaan, sille wy bestannen fan HDFS lêze mei in ienfâldige multi-threaded applikaasje. Stel de parameters yn:

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

En lês gewoan de bestannen:

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

Dizze koade wurdt útfierd yn aparte triedden en wy sille it oantal tagelyk lêzen bestannen ferheegje (fan 10 nei 200 - horizontale as) en it oantal caches (fan 1 oant 10 - grafiken). De fertikale as lit de fersnelling sjen dy't resultaat is fan in ferheging fan SSC relatyf oan it gefal as d'r mar ien cache is.

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Hoe kinne jo de grafyk lêze: De útfieringstiid foar 100 tûzen lêzen yn 64 KB-blokken mei ien cache fereasket 78 sekonden. Wylst it mei 5 caches 16 sekonden nimt. Dy. der is in fersnelling fan ~5 kear. Sa't út 'e grafyk te sjen is, is it effekt net heul merkber foar in lyts oantal parallelle lêzings, it begjint in merkbere rol te spyljen as d'r mear as 50 thread-lêzen binne. en boppe jout in signifikant lytsere prestaasjesferheging.

Opmerking 1: om't de testresultaten frij flechtich binne (sjoch hjirûnder), waarden 3 runs útfierd en de resultearjende wearden waarden gemiddeld.

Opmerking 2: De prestaasjeswinst fan it konfigurearjen fan willekeurige tagong is itselde, hoewol de tagong sels wat stadiger is.

It is lykwols needsaaklik om te ferdúdlikjen dat, yn tsjinstelling ta it gefal mei HBase, dizze fersnelling net altyd fergees is. Hjir "ûntskoattelje" wy it fermogen fan 'e CPU om mear wurk te dwaan, ynstee fan op slot te hingjen.

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Hjir kinne jo observearje dat, yn 't algemien, in tanimming fan it oantal caches in sawat evenredige ferheging fan CPU-gebrûk jout. Lykwols, der binne wat mear winnende kombinaasjes.

Bygelyks, lit ús nimme in tichterby op 'e ynstelling SSC = 3. De ferheging fan prestaasjes op it berik is oer 3.3 kear. Hjirûnder binne de resultaten fan alle trije aparte runs.

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Wylst CPU konsumpsje nimt ta mei likernôch 2.8 kear. It ferskil is net hiel grut, mar lytse Greta is al bliid en kin tiid hawwe om nei skoalle te gean en les te nimmen.

Sa sil dit in posityf effekt hawwe foar elk ark dat gebrûk makket fan bulk tagong ta HDFS (bygelyks Spark, ensfh.), op betingst dat de applikaasjekoade lichtgewicht is (dus de plug is oan 'e HDFS-kliïntkant) en d'r is fergees CPU-krêft . Om te kontrolearjen, litte wy testen hokker effekt it kombineare gebrûk fan BlockCache-optimalisaasje en SSC-tuning foar lêzen fan HBase sil hawwe.

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

It kin sjoen wurde dat ûnder sokke betingsten it effekt net sa grut is as yn ferfine testen (lêzen sûnder ferwurking), mar it is heul mooglik om hjir in ekstra 80K út te drukken. Tegearre leverje beide optimisaasjes oant 4x fersnelling.

Foar dizze optimalisaasje is ek in PR makke [HDFS-15202], dat is gearfoege en dizze funksjonaliteit sil beskikber wêze yn takomstige releases.

En úteinlik wie it nijsgjirrich om de lêsprestaasjes te fergelykjen fan in ferlykbere database mei brede kolommen, Cassandra en HBase.

Om dit te dwaan, lansearren wy eksimplaren fan it standert YCSB load testing utility fan twa hosts (yn totaal 800 threads). Oan 'e serverkant - 4 eksimplaren fan RegionServer en Cassandra op 4 hosts (net dejingen wêr't de kliïnten rinne, om har ynfloed te foarkommen). Lêzingen kamen út tabellen fan grutte:

HBase - 300 GB op HDFS (100 GB suvere gegevens)

Cassandra - 250 GB (replikaasjefaktor = 3)

Dy. de folume wie likernôch itselde (yn HBase in bytsje mear).

HBase parameters:

dfs.client.short.circuit.num = 5 (HDFS-kliïntoptimalisaasje)

hbase.lru.cache.heavy.eviction.count.limit = 30 - dit betsjut dat de patch sil begjinne te wurkjen nei 30 útsettings (~ 5 minuten)

hbase.lru.cache.heavy.eviction.mb.size.limit = 300 - Doelvolume fan caching en útsetting

YCSB-logs waarden parseard en kompilearre yn Excel-grafiken:

Hoe kin de lêssnelheid fan HBase oant 3 kear ferheegje en fan HDFS oant 5 kear

Sa't jo sjen kinne, meitsje dizze optimisaasjes it mooglik om de prestaasjes fan dizze databases ûnder dizze betingsten te fergelykjen en 450 tûzen lêzen per sekonde te berikken.

Wy hoopje dat dizze ynformaasje nuttich kin wêze foar ien yn 'e spannende striid foar produktiviteit.

Boarne: www.habr.com

Add a comment