HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Үлкен деректермен жұмыс істеу кезінде жоғары өнімділік негізгі талаптардың бірі болып табылады. Сбербанктегі деректерді жүктеу бөлімінде біз Hadoop негізіндегі деректер бұлтына барлық дерлік транзакцияларды енгіземіз, сондықтан ақпараттың шынымен үлкен ағындарымен айналысамыз. Әрине, біз әрқашан өнімділікті жақсарту жолдарын іздейміз, енді біз RegionServer HBase және HDFS клиентін қалай түзетіп алғанымызды айтқымыз келеді, соның арқасында оқу операцияларының жылдамдығын айтарлықтай арттыра алдық.
HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Дегенмен, жақсартулардың мәніне көшпес бұрын, егер сіз HDD-де отырсаңыз, негізінен айналып өтуге болмайтын шектеулер туралы айту керек.

Неліктен HDD және жылдам Random Access оқулары үйлесімсіз
Өздеріңіз білетіндей, HBase және басқа да көптеген деректер базалары деректерді бірнеше ондаған килобайттық блоктарда сақтайды. Әдепкі бойынша ол шамамен 64 КБ құрайды. Енді бізге тек 100 байт алу керек деп елестетіп көрейік және біз HBase-ден белгілі бір кілт арқылы бізге бұл деректерді беруін сұраймыз. HFiles ішіндегі блок өлшемі 64 КБ болғандықтан, сұрау қажет болғаннан 640 есе үлкен (бар болғаны бір минут!) болады.

Әрі қарай, сұрау HDFS және оның метадеректерін кэштеу механизмі арқылы өтетіндіктен ShortCircuitCache (бұл файлдарға тікелей қол жеткізуге мүмкіндік береді), бұл дискіден 1 МБ оқуға әкеледі. Дегенмен, бұл параметр арқылы реттеуге болады dfs.client.read.қысқа тұйықталу.буфер.өлшемі және көптеген жағдайларда бұл мәнді, мысалы, 126 КБ-қа дейін азайту мағынасы бар.

Біз мұны істейміз делік, бірақ оған қоса, FileChannel.read сияқты функциялар сияқты java api арқылы деректерді оқи бастағанда және операциялық жүйеден көрсетілген деректер көлемін оқуды сұрағанда, ол «болған жағдайда» 2 есе көп оқиды. , яғни. Біздің жағдайда 256 КБ. Себебі java-да бұл әрекетті болдырмау үшін FADV_RANDOM жалауын орнатудың оңай жолы жоқ.

Нәтижесінде, біздің 100 байтты алу үшін капюшонның астында 2600 есе көп оқылады. Шешім анық сияқты, блок өлшемін килобайтқа дейін азайтып, аталған жалаушаны орнатып, үлкен ағартушылық жеделдетуге ие болайық. Бірақ қиындық мынада, блок өлшемін 2 есе азайту арқылы біз уақыт бірлігіне оқылатын байттардың санын 2 есе азайтамыз.

FADV_RANDOM жалауын орнатудан біраз пайда алуға болады, бірақ тек жоғары көп ағынды және 128 КБ блок өлшемімен ғана алуға болады, бірақ бұл ең көбі бірнеше ондаған пайызды құрайды:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Сынақтар әрқайсысы 100 ГБ өлшемді және 1 HDD-де орналасқан 10 файлда жүргізілді.

Осы жылдамдықта нені есептей алатынымызды есептеп көрейік:
Айталық, біз 10 дискіден 280 МБ/сек жылдамдықпен оқыдық, яғни. 3 миллион есе 100 байт. Бірақ есімізде, бізге қажет деректер оқылғаннан 2600 есе аз. Осылайша, 3 миллионды 2600-ге бөліп, аламыз Секундына 1100 жазба.

Көңіл күйзелтеді, солай емес пе? Табиғат солай Кездейсоқ қол жетімділік HDD-дегі деректерге қол жеткізу - блок өлшеміне қарамастан. Бұл кездейсоқ қол жеткізудің физикалық шегі және мұндай жағдайларда ешбір дерекқор одан да көп сыға алмайды.

Деректер базасы қалай әлдеқайда жоғары жылдамдыққа жетеді? Бұл сұраққа жауап беру үшін келесі суретте не болып жатқанын қарастырайық:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Мұнда біз алғашқы бірнеше минутта жылдамдық секундына мыңға жуық рекордты құрайтынын көреміз. Дегенмен, сұралғаннан әлдеқайда көп оқылатындықтан, деректер операциялық жүйенің (linux) буфінде/кэшінде аяқталады және жылдамдығы секундына 60 мыңға дейін артады.

Осылайша, біз тек ОЖ кэшінде немесе салыстырмалы қол жеткізу жылдамдығының SSD/NVMe сақтау құрылғыларында орналасқан деректерге қол жеткізуді жеделдетумен айналысамыз.

Біздің жағдайда біз сынақтарды 4 серверден тұратын стендте жүргіземіз, олардың әрқайсысы келесідей зарядталады:

Орталық процессор: Xeon E5-2680 v4 @ 2.40 ГГц 64 ағын.
Жад: 730 ГБ.
java нұсқасы: 1.8.0_111

Және бұл жерде негізгі мәселе - кестелердегі оқуды қажет ететін деректер көлемі. Шын мәнінде, егер сіз толығымен HBase кэшінде орналасқан кестеден деректерді оқысаңыз, ол операциялық жүйенің буф/кэшінен де оқуға келмейді. Өйткені HBase әдепкі бойынша жадтың 40% BlockCache деп аталатын құрылымға бөледі. Негізінде бұл ConcurrentHashMap, мұнда кілт файл атауы + блоктың ығысуы, ал мән осы ығысудағы нақты деректер болып табылады.

Осылайша, тек осы құрылымнан оқығанда, біз тамаша жылдамдықты көреміз, секундына миллион сұрау сияқты. Бірақ біз тек дерекқор қажеттіліктері үшін жүздеген гигабайт жадты бөле алмайтынымызды елестетіп көрейік, өйткені бұл серверлерде басқа да көптеген пайдалы нәрселер жұмыс істейді.

Мысалы, біздің жағдайда бір RS-тегі BlockCache көлемі шамамен 12 ГБ құрайды. Біз бір түйінге екі РС қондырдық, яғни. Барлық түйіндерде BlockCache үшін 96 ГБ бөлінген. Және бірнеше есе көп деректер бар, мысалы, бұл 4 кесте болсын, әрқайсысы 130 аймақ, онда файлдардың өлшемі 800 МБ, FAST_DIFF арқылы қысылады, яғни. жалпы көлемі 410 ГБ (бұл таза деректер, яғни репликация факторын есепке алмағанда).

Осылайша, BlockCache жалпы деректер көлемінің шамамен 23% құрайды және бұл BigData деп аталатын нақты жағдайларға әлдеқайда жақын. Міне, қызық осы жерден басталады - өйткені кэшке кіру саны неғұрлым аз болса, өнімділік соғұрлым нашар болады. Өйткені, егер сіз сағынып қалсаңыз, сіз көп жұмыс істеуге тура келеді - яғни. Жүйе функцияларын шақыруға өтіңіз. Дегенмен, мұны болдырмау мүмкін емес, сондықтан мүлде басқа аспектіні қарастырайық - кэш ішіндегі деректермен не болады?

Жағдайды жеңілдетейік және бізде тек 1 нысанға сәйкес келетін кэш бар деп есептейік. Кэштен 3 есе үлкен деректер көлемімен жұмыс істеуге тырысқанда не болатынын мысал ретінде келтіреміз, бізге қажет болады:

1. 1-блокты кэшке орналастырыңыз
2. 1-блокты кэштен алып тастаңыз
3. 2-блокты кэшке орналастырыңыз
4. 2-блокты кэштен алып тастаңыз
5. 3-блокты кэшке орналастырыңыз

5 әрекет орындалды! Дегенмен, бұл жағдайды қалыпты деп атауға болмайды; шын мәнінде, біз HBase-ді мүлдем пайдасыз жұмыстарды орындауға мәжбүрлеп жатырмыз. Ол үнемі операциялық жүйе кэшінен деректерді оқиды, оны BlockCache ішіне орналастырады, тек деректердің жаңа бөлігі келгендіктен оны бірден лақтырып тастайды. Посттың басындағы анимация мәселенің мәнін көрсетеді - қоқыс жинаушы масштабтан шығып жатыр, атмосфера қызып жатыр, алыс және ыстық Швециядағы кішкентай Грета ренжіді. Ал біз IT мамандары балалардың қайғылы болғанын ұнатпаймыз, сондықтан біз бұл туралы не істей алатынымызды ойлай бастаймыз.

Кэш толып кетпеуі үшін барлық блоктарды емес, олардың белгілі бір пайызын ғана қойсаңыз ше? Деректерді BlockCache ішіне қою үшін функцияның басына жай ғана бірнеше код жолын қосу арқылы бастайық:

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

Мұндағы мәселе мынада: офсет – файлдағы блоктың орны және оның соңғы сандары 00-ден 99-ға дейін кездейсоқ және біркелкі таратылады. Сондықтан біз тек бізге қажет диапазонға түсетіндерді өткізіп жібереміз.

Мысалы, cacheDataBlockPercent = 20 мәнін орнатыңыз және не болатынын көріңіз:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Нәтижесі анық. Төмендегі графиктерде мұндай жеделдеудің неліктен орын алғаны анық болады - біз деректерді кэшке орналастырудың Sisyphean жұмысын жасамай-ақ, оны марс иттерінің ағынына бірден лақтырып тастау үшін көп GC ресурстарын үнемдейміз:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Сонымен қатар, процессорды пайдалану артады, бірақ өнімділіктен әлдеқайда аз:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Сондай-ақ, BlockCache-де сақталған блоктардың әртүрлі екенін атап өткен жөн. Көпшілігі, шамамен 95% - деректердің өзі. Ал қалғаны метадеректер, мысалы Bloom сүзгілері немесе LEAF_INDEX және және т.б.. Бұл деректер жеткіліксіз, бірақ бұл өте пайдалы, өйткені деректерге тікелей қол жеткізбес бұрын, HBase бұл жерде әрі қарай іздеу қажет пе, жоқ па, және егер болса, қызығушылық блогы дәл қай жерде орналасқанын түсіну үшін метаға жүгінеді.

Сондықтан кодта біз тексеру шартын көреміз buf.getBlockType().isData() және осы метаның арқасында біз оны кез келген жағдайда кэште қалдырамыз.

Енді жүктемені арттырып, функцияны бір әрекетте сәл қатайтайық. Бірінші сынақта біз кесу пайызын = 20 етіп жасадық және BlockCache аздап пайдаланылмады. Енді оны 23% етіп орнатып, қанықтылықтың қай нүктеде болатынын көру үшін әрбір 100 минут сайын 5 ағынды қосып көрейік:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Мұнда біз түпнұсқа нұсқасының секундына шамамен 100 мың сұраныспен төбеге дерлік соғатынын көреміз. Ал патч 300 мыңға дейін жеделдету береді. Сонымен қатар, одан әрі жеделдету енді соншалықты «тегін» емес екені анық, процессорды пайдалану да артып келеді.

Дегенмен, бұл өте талғампаз шешім емес, өйткені біз блоктардың қанша пайызын кэштеу керек екенін алдын ала білмейміз, бұл жүктеме профиліне байланысты. Сондықтан оқу операцияларының белсенділігіне байланысты бұл параметрді автоматты түрде реттеу механизмі жүзеге асырылды.

Мұны басқару үшін үш опция қосылды:

hbase.lru.cache.heavy.evction.count.limit — оңтайландыруды пайдалануды бастамас бұрын (яғни блоктарды өткізіп жіберу) кэштен деректерді шығару процесі қанша рет орындалатынын белгілейді. Әдепкі бойынша ол MAX_INT = 2147483647 мәніне тең және іс жүзінде мүмкіндік ешқашан осы мәнмен жұмыс істемейтінін білдіреді. Өйткені шығару процесі әрбір 5 - 10 секунд сайын басталады (бұл жүктемеге байланысты) және 2147483647 * 10/60/60/24/365 = 680 жыл. Дегенмен, біз бұл параметрді 0-ге орнатып, мүмкіндікті іске қосқаннан кейін бірден жұмыс істей аламыз.

Дегенмен, бұл параметрде пайдалы жүктеме де бар. Егер біздің жүктемеміз қысқа мерзімді оқулар (айталық, күндізгі уақытта) және ұзақ мерзімді оқулар (түнде) үнемі араласып тұратындай болса, онда ұзақ оқу әрекеттері орындалып жатқанда ғана мүмкіндіктің қосылғанына көз жеткізе аламыз.

Мысалы, қысқа мерзімді оқулар әдетте шамамен 1 минутқа созылатынын білеміз. Блоктарды лақтыруды бастаудың қажеті жоқ, кэш ескіруге уақыт болмайды, содан кейін біз бұл параметрді, мысалы, 10-ға теңестіре аламыз. Бұл оңтайландыру ұзақ уақыт болған кезде ғана жұмыс істей бастайды. белсенді оқу термині басталды, яғни. 100 секундта. Осылайша, егер бізде қысқа мерзімді оқу болса, онда барлық блоктар кэшке түседі және қол жетімді болады (стандартты алгоритммен шығарылатындардан басқа). Ұзақ мерзімді оқуларды орындаған кезде, мүмкіндік қосылады және біз әлдеқайда жоғары өнімділікке ие болар едік.

hbase.lru.cache.heavy.evction.mb.size.limit — 10 секунд ішінде кэшке қанша мегабайт орналастырғымыз келетінін (және, әрине, шығаруды) белгілейді. Мүмкіндік осы мәнге жетуге және оны сақтауға тырысады. Мәселе мынада: егер біз гигабайттарды кэшке итерсек, онда гигабайттарды шығаруға тура келеді, және бұл, жоғарыда көргеніміздей, өте қымбат. Дегенмен, сіз оны тым кішкентай етіп орнатуға тырыспауыңыз керек, себебі бұл блокты өткізіп жіберу режимінің мерзімінен бұрын шығуына әкеледі. Қуатты серверлер үшін (шамамен 20-40 физикалық ядро) шамамен 300-400 МБ орнату оңтайлы. Орта сынып үшін (~10 ядро) 200-300 МБ. Әлсіз жүйелер үшін (2-5 ядролар) 50-100 МБ қалыпты болуы мүмкін (оларда тексерілмеген).

Мұның қалай жұмыс істейтінін қарастырайық: hbase.lru.cache.heavy.eviction.mb.size.limit = 500 орнаттық делік, қандай да бір жүктеме (оқу) бар, содан кейін ~10 секунд сайын біз қанша байт болғанын есептейміз. формула бойынша шығарылады:

Үстеме шығыс = Босатылған байттар сомасы (МБ) * 100 / Шектеу (МБ) - 100;

Егер шын мәнінде 2000 МБ шығарылған болса, онда үстеме шығындар мынаған тең:

2000 * 100 / 500 - 100 = 300%

Алгоритмдер бірнеше ондаған пайыздан аспауға тырысады, сондықтан мүмкіндік кэштелген блоктардың пайызын азайтады, осылайша автоматты реттеу механизмін жүзеге асырады.

Дегенмен, егер жүктеме төмендесе, тек 200 МБ шығарылды делік және үстеме шығын теріс болады (артық кету деп аталады):

200 * 100 / 500 - 100 = -60%

Керісінше, бұл мүмкіндік кэштелген блоктардың пайызын үстеме баға оң болғанша арттырады.

Төменде бұл нақты деректерде қалай көрінетінінің мысалы келтірілген. 0% жетуге тырысудың қажеті жоқ, мүмкін емес. Ол шамамен 30 - 100% болғанда өте жақсы, бұл қысқа мерзімді асқынулар кезінде оңтайландыру режимінен мерзімінен бұрын шығуды болдырмауға көмектеседі.

hbase.lru.cache.heavy.exiction.overhead.коэффиценті — нәтижеге қаншалықты тез қол жеткізгіміз келетінін белгілейді. Біздің оқуларымыз негізінен ұзақ және күткіміз келмейтінін нақты білсек, біз бұл қатынасты арттырып, жоғары өнімділікке тезірек қол жеткізе аламыз.

Мысалы, біз бұл коэффициент = 0.01 орнаттық. Бұл үстеме шығындар (жоғарыдан қараңыз) алынған нәтиже бойынша осы санға көбейтілетінін және кэштелген блоктардың пайызы төмендейтінін білдіреді. Үстеме шығындар = 300% және коэффициент = 0.01 деп есептейік, сонда кэштелген блоктардың пайызы 3% азаяды.

Ұқсас «Артқы қысым» логикасы теріс үстеме (артып кету) мәндері үшін де жүзеге асырылады. Оқу және шығару көлемінің қысқа мерзімді ауытқуы әрқашан мүмкін болғандықтан, бұл механизм оңтайландыру режимінен мерзімінен бұрын шығуды болдырмауға мүмкіндік береді. Кері қысымның инверттелген логикасы бар: асып кету неғұрлым күшті болса, соғұрлым көп блоктар кэштеледі.

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Іске асыру коды

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

Енді осының бәрін нақты мысал арқылы қарастырайық. Бізде келесі сынақ сценарийі бар:

  1. Сканерлеуді бастайық (25 ағын, топтама = 100)
  2. 5 минуттан кейін көп алуды қосыңыз (25 ағын, топтама = 100)
  3. 5 минуттан кейін көп алуды өшіріңіз (тек сканерлеу қайта қалады)

Біз екі іске қосамыз, алдымен hbase.lru.cache.heavy.eviction.count.limit = 10000 (бұл мүмкіндікті шынымен өшіреді), содан кейін шектеу = 0 (оны қосады) орнатыңыз.

Төмендегі журналдарда біз мүмкіндіктің қалай қосылғанын көреміз және Overshooting параметрін 14-71% қалпына келтіреміз. Уақыт өте жүктеме азаяды, бұл кері қысымды қосады және HBase көбірек блоктарды қайтадан кэштейді.

Log RegionServer
шығарылған (МБ): 0, қатынасы 0.0, үстеме шығындар (%): -100, ауыр шығару есептегіші: 0, ағымдағы кэштеу DataBlock (%): 100
шығарылған (МБ): 0, қатынасы 0.0, үстеме шығындар (%): -100, ауыр шығару есептегіші: 0, ағымдағы кэштеу DataBlock (%): 100
шығарылған (МБ): 2170, қатынасы 1.09, үстеме шығындар (%): 985, ауыр шығару есептегіші: 1, ағымдағы кэштеу DataBlock (%): 91 < бастау
шығарылған (МБ): 3763, қатынасы 1.08, үстеме шығындар (%): 1781, ауыр көшіру есептегіші: 2, ағымдағы кэштеу DataBlock (%): 76
шығарылған (МБ): 3306, қатынасы 1.07, үстеме шығындар (%): 1553, ауыр көшіру есептегіші: 3, ағымдағы кэштеу DataBlock (%): 61
шығарылған (МБ): 2508, қатынасы 1.06, үстеме шығындар (%): 1154, ауыр көшіру есептегіші: 4, ағымдағы кэштеу DataBlock (%): 50
шығарылған (МБ): 1824, қатынасы 1.04, үстеме шығындар (%): 812, ауыр көшіру есептегіші: 5, ағымдағы кэштеу DataBlock (%): 42
шығарылған (МБ): 1482, қатынасы 1.03, үстеме шығындар (%): 641, ауыр көшіру есептегіші: 6, ағымдағы кэштеу DataBlock (%): 36
шығарылған (МБ): 1140, қатынасы 1.01, үстеме шығындар (%): 470, ауыр көшіру есептегіші: 7, ағымдағы кэштеу DataBlock (%): 32
шығарылған (МБ): 913, қатынасы 1.0, үстеме шығындар (%): 356, ауыр көшіру есептегіші: 8, ағымдағы кэштеу DataBlock (%): 29
шығарылған (МБ): 912, қатынасы 0.89, үстеме шығындар (%): 356, ауыр көшіру есептегіші: 9, ағымдағы кэштеу DataBlock (%): 26
шығарылған (МБ): 684, қатынасы 0.76, үстеме шығындар (%): 242, ауыр көшіру есептегіші: 10, ағымдағы кэштеу DataBlock (%): 24
шығарылған (МБ): 684, қатынасы 0.61, үстеме шығындар (%): 242, ауыр көшіру есептегіші: 11, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 456, қатынасы 0.51, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 12, ағымдағы кэштеу DataBlock (%): 21
шығарылған (МБ): 456, қатынасы 0.42, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 13, ағымдағы кэштеу DataBlock (%): 20
шығарылған (МБ): 456, қатынасы 0.33, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 14, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 15, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 342, қатынасы 0.32, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 16, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 342, қатынасы 0.31, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 17, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.3, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 18, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.29, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 19, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.27, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 20, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.25, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 21, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.24, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 22, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.22, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 23, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.21, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 24, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.2, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 25, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 228, қатынасы 0.17, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 26, ағымдағы кэштеу DataBlock (%): 19
шығарылды (МБ): 456, қатынасы 0.17, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 27, ағымдағы кэштеу DataBlock (%): 18 < қосылды (бірақ кесте бірдей)
шығарылған (МБ): 456, қатынасы 0.15, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 28, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 342, қатынасы 0.13, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 29, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 342, қатынасы 0.11, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 30, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 342, қатынасы 0.09, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 31, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 228, қатынасы 0.08, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 32, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 228, қатынасы 0.07, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 33, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 228, қатынасы 0.06, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 34, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 228, қатынасы 0.05, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 35, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 228, қатынасы 0.05, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 36, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 228, қатынасы 0.04, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 37, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 109, қатынасы 0.04, үстеме шығындар (%): -46, ауыр шығару есептегіші: 37, ағымдағы кэштеу DataBlock (%): 22 < кері қысым
шығарылған (МБ): 798, қатынасы 0.24, үстеме шығындар (%): 299, ауыр көшіру есептегіші: 38, ағымдағы кэштеу DataBlock (%): 20
шығарылған (МБ): 798, қатынасы 0.29, үстеме шығындар (%): 299, ауыр көшіру есептегіші: 39, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 570, қатынасы 0.27, үстеме шығындар (%): 185, ауыр көшіру есептегіші: 40, ағымдағы кэштеу DataBlock (%): 17
шығарылған (МБ): 456, қатынасы 0.22, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 41, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 342, қатынасы 0.16, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 42, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 342, қатынасы 0.11, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 43, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 228, қатынасы 0.09, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 44, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 228, қатынасы 0.07, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 45, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 228, қатынасы 0.05, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 46, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 222, қатынасы 0.04, үстеме шығындар (%): 11, ауыр көшіру есептегіші: 47, ағымдағы кэштеу DataBlock (%): 16
шығарылған (МБ): 104, қатынасы 0.03, үстеме шығындар (%): -48, ауыр шығару есептегіші: 47, ағымдағы кэштеу DataBlock (%): 21 < үзу алады
шығарылған (МБ): 684, қатынасы 0.2, үстеме шығындар (%): 242, ауыр көшіру есептегіші: 48, ағымдағы кэштеу DataBlock (%): 19
шығарылған (МБ): 570, қатынасы 0.23, үстеме шығындар (%): 185, ауыр көшіру есептегіші: 49, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 342, қатынасы 0.22, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 50, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 228, қатынасы 0.21, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 51, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 228, қатынасы 0.2, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 52, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 228, қатынасы 0.18, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 53, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 228, қатынасы 0.16, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 54, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 228, қатынасы 0.14, үстеме шығындар (%): 14, ауыр көшіру есептегіші: 55, ағымдағы кэштеу DataBlock (%): 18
шығарылған (МБ): 112, қатынасы 0.14, үстеме шығындар (%): -44, ауыр шығару есептегіші: 55, ағымдағы кэштеу DataBlock (%): 23 < кері қысым
шығарылған (МБ): 456, қатынасы 0.26, үстеме шығындар (%): 128, ауыр көшіру есептегіші: 56, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.31, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 57, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 58, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 59, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 60, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 61, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 62, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 63, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.32, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 64, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 65, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 66, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.32, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 67, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 68, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.32, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 69, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.32, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 70, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 71, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 72, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 73, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 74, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 75, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 342, қатынасы 0.33, үстеме шығындар (%): 71, ауыр көшіру есептегіші: 76, ағымдағы кэштеу DataBlock (%): 22
шығарылған (МБ): 21, қатынасы 0.33, үстеме шығындар (%): -90, ауыр шығару есептегіші: 76, ағымдағы кэштеу DataBlock (%): 32
шығарылған (МБ): 0, қатынасы 0.0, үстеме шығындар (%): -100, ауыр шығару есептегіші: 0, ағымдағы кэштеу DataBlock (%): 100
шығарылған (МБ): 0, қатынасы 0.0, үстеме шығындар (%): -100, ауыр шығару есептегіші: 0, ағымдағы кэштеу DataBlock (%): 100

Сканерлеулер бірдей процесті екі кэш бөлімдері арасындағы қатынас графигі түрінде көрсету үшін қажет болды - жалғыз (бұрын ешқашан сұралмаған блоктар) және көп (бұл жерде кемінде бір рет «сұралған» деректер сақталады):

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Соңында, параметрлердің жұмысы график түрінде қалай көрінеді. Салыстыру үшін, басында кэш толығымен өшірілді, содан кейін HBase кэштеумен іске қосылды және оңтайландыру жұмысының басталуын 5 минутқа (30 шығару циклі) кешіктірді.

Толық кодты тарту сұрауынан табуға болады HBASE 23887 github сайтында.

Дегенмен, секундына 300 мың оқу осы шарттарда бұл жабдықта қол жеткізуге болатын нәрсе емес. HDFS арқылы деректерге қол жеткізу қажет болғанда, желілік өзара әрекеттесуді болдырмай, деректерге тікелей қол жеткізуге мүмкіндік беретін ShortCircuitCache (бұдан әрі - SSC) механизмі пайдаланылады.

Профильдеу көрсеткендей, бұл механизм үлкен пайда әкелсе де, ол белгілі бір уақытта тығырыққа айналады, өйткені барлық дерлік ауыр операциялар құлыптың ішінде жүреді, бұл көп уақытты бұғаттауға әкеледі.

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Мұны түсініп, біз тәуелсіз SSC массивін құру арқылы мәселені айналып өтуге болатынын түсіндік:

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

Содан кейін соңғы ығысу санындағы қиылыстарды қоспағанда, олармен жұмыс істеңіз:

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

Енді тестілеуді бастауға болады. Ол үшін HDFS файлдарын қарапайым көп ағынды қолданба арқылы оқимыз. Параметрлерді орнатыңыз:

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

Және жай ғана файлдарды оқыңыз:

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

Бұл код жеке ағындарда орындалады және біз бір уақытта оқылатын файлдардың санын (10-нан 200-ге дейін - көлденең ось) және кэштер санын (1-ден 10-ға дейін - графика) көбейтеміз. Тік ось тек бір кэш болған жағдайға қатысты SSC ұлғаюынан туындайтын жеделдеуді көрсетеді.

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Графикті оқу әдісі: Бір кэшпен 100 КБ блоктарда 64 мың оқуды орындау уақыты 78 секундты қажет етеді. Ал 5 кэшпен 16 секунд кетеді. Анау. ~5 есе үдеу бар. Графиктен көрініп тұрғандай, параллельді оқулардың аз саны үшін әсер онша байқалмайды, ол 50-ден астам оқу ағыны болған кезде байқалатын рөл атқара бастайды.Сонымен қатар, SSC санының 6-дан ұлғаюы байқалады. және одан жоғары өнімділікті айтарлықтай азырақ арттырады.

1-ескертпе: сынақ нәтижелері айтарлықтай өзгермелі болғандықтан (төменде қараңыз), 3 жүгіріс орындалды және алынған мәндер орташа алынды.

2-ескертпе: Кездейсоқ қол жеткізуді конфигурациялаудың өнімділігі бірдей, дегенмен қол жеткізудің өзі сәл баяуырақ.

Дегенмен, HBase жағдайынан айырмашылығы, бұл жеделдету әрқашан бос емес екенін түсіндіру қажет. Мұнда біз орталық процессордың құлыптарға ілінбей, көбірек жұмыс істеу мүмкіндігін «құлпын ашамыз».

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Мұнда сіз жалпы кэштер санының ұлғаюы процессорды пайдаланудың шамамен пропорционалды өсуін беретінін байқауға болады. Дегенмен, жеңіске жететін комбинациялар бар.

Мысалы, SSC = 3 параметрін егжей-тегжейлі қарастырайық. Диапазондағы өнімділіктің артуы шамамен 3.3 есе. Төменде барлық үш бөлек жүгірудің нәтижелері берілген.

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Процессорды тұтыну шамамен 2.8 есе артады. Айырмашылық онша үлкен емес, бірақ кішкентай Грета қазірдің өзінде бақытты және мектепке баруға және сабақ алуға уақыты болуы мүмкін.

Осылайша, бұл қолданба коды жеңіл (яғни, штепсель HDFS клиент жағында) және процессордың бос қуаты болған жағдайда, HDFS-ке жаппай қол жеткізуді пайдаланатын кез келген құралға (мысалы, Spark және т.б.) оң әсер етеді. . Тексеру үшін HBase-тен оқу үшін BlockCache оңтайландыруы мен SSC баптауын біріктіріп пайдалану қандай әсер ететінін тексеріп көрейік.

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Көріп отырғандай, мұндай жағдайларда әсер тазартылған сынақтардағыдай үлкен емес (ешқандай өңдеусіз оқу), бірақ мұнда қосымша 80K сығуға болады. Екі оңтайландыру бірге 4 есе жылдамдатуды қамтамасыз етеді.

Бұл оңтайландыру үшін PR да жасалды [HDFS-15202], ол біріктірілді және бұл функция болашақ шығарылымдарда қолжетімді болады.

Соңында, Cassandra және HBase сияқты кең бағанды ​​дерекқордың оқу өнімділігін салыстыру қызықты болды.

Мұны істеу үшін біз екі хосттан (барлығы 800 ағын) стандартты YCSB жүктеме сынау утилитасының даналарын іске қостық. Сервер жағында - 4 хосттағы RegionServer және Cassandra 4 данасы (олардың ықпалын болдырмау үшін клиенттер жұмыс істейтіндер емес). Көрсеткіштер өлшем кестелерінен алынды:

HBase – HDFS жүйесінде 300 ГБ (100 ГБ таза деректер)

Кассандра - 250 ГБ (репликация коэффициенті = 3)

Анау. көлемі шамамен бірдей болды (HBase-де сәл көбірек).

HBase параметрлері:

dfs.client.short.circuit.num = 5 (HDFS клиентін оңтайландыру)

hbase.lru.cache.heavy.eviction.count.limit = 30 - бұл патч 30 шығарудан кейін жұмыс істей бастайтынын білдіреді (~5 минут)

hbase.lru.cache.heavy.eviction.mb.size.limit = 300 — кэштеу мен шығарудың мақсатты көлемі

YCSB журналдары талданды және Excel графиктеріне құрастырылды:

HBase-тен оқу жылдамдығын 3 есеге дейін және HDFS-тен 5 есеге дейін қалай арттыруға болады

Көріп отырғаныңыздай, бұл оңтайландырулар осы шарттарда осы дерекқорлардың өнімділігін салыстыруға және секундына 450 мың оқуға қол жеткізуге мүмкіндік береді.

Бұл ақпарат өнімділік үшін қызықты күрес кезінде біреуге пайдалы болады деп үміттенеміз.

Ақпарат көзі: www.habr.com

пікір қалдыру