HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Böyük verilənlərlə işləyərkən yüksək performans əsas tələblərdən biridir. Sberbank-da məlumatların yüklənməsi departamentində biz demək olar ki, bütün əməliyyatları Hadoop-a əsaslanan Məlumat Buludumuza nəql edirik və buna görə də həqiqətən böyük məlumat axını ilə məşğul oluruq. Təbii ki, biz həmişə performansı yaxşılaşdırmağın yollarını axtarırıq və indi sizə RegionServer HBase və HDFS müştərisini necə düzəldə bildiyimizi söyləmək istəyirik, bunun sayəsində oxu əməliyyatlarının sürətini əhəmiyyətli dərəcədə artıra bilmişik.
HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Bununla birlikdə, təkmilləşdirmələrin mahiyyətinə keçməzdən əvvəl, bir HDD-də otursanız, prinsipcə qaçmaq mümkün olmayan məhdudiyyətlər haqqında danışmağa dəyər.

Nə üçün HDD və sürətli Random Access oxunuşları uyğun gəlmir
Bildiyiniz kimi, HBase və bir çox başqa verilənlər bazası məlumatları bir neçə on kilobaytlıq bloklarda saxlayır. Varsayılan olaraq, təxminən 64 KB-dir. İndi təsəvvür edək ki, yalnız 100 bayt əldə etməliyik və biz HBase-dən müəyyən bir açardan istifadə edərək bu məlumatları bizə verməsini xahiş edirik. HFiles-da blok ölçüsü 64 KB olduğundan, sorğu lazım olduğundan 640 dəfə böyük olacaq (cəmi bir dəqiqə!).

Sonra, sorğu HDFS və onun metadata keşləmə mexanizmindən keçəcəyi üçün ShortCircuitCache (fayllara birbaşa çıxış imkanı verir), bu, diskdən artıq 1 MB oxumağa gətirib çıxarır. Bununla belə, bu parametrlə tənzimlənə bilər dfs.client.read.qısaqapanma.bufer.ölçüsü və bir çox hallarda bu dəyəri, məsələn, 126 KB-a qədər azaltmağın mənası var.

Tutaq ki, biz bunu edirik, lakin əlavə olaraq, FileChannel.read kimi funksiyalar kimi məlumatları java api vasitəsilə oxumağa başlayanda və əməliyyat sistemindən müəyyən edilmiş miqdarda məlumatı oxumağı xahiş etdikdə, o, “hər halda” 2 dəfə çox oxuyur. , yəni. Bizim vəziyyətimizdə 256 KB. Bunun səbəbi, java-da bu davranışın qarşısını almaq üçün FADV_RANDOM bayrağını təyin etmək üçün asan bir yol yoxdur.

Nəticədə, 100 baytımızı əldə etmək üçün başlıq altında 2600 dəfə çox oxunur. Belə görünür ki, həll yolu göz qabağındadır, gəlin blok ölçüsünü bir kilobayta qədər azaldaq, qeyd olunan bayrağı təyin edək və böyük maarifləndirmə sürəti qazanaq. Ancaq problem ondadır ki, blokun ölçüsünü 2 dəfə azaltmaqla, vaxt vahidinə oxunan baytların sayını da 2 dəfə azaldırıq.

FADV_RANDOM bayrağının qurulmasından müəyyən qazanc əldə etmək olar, lakin yalnız yüksək çoxillik və 128 KB blok ölçüsü ilə, lakin bu, maksimum bir neçə on faizdir:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Testlər hər biri 100 GB ölçülü və 1 HDD-də yerləşən 10 fayl üzərində aparılıb.

Prinsipcə, bu sürətlə nəyə arxalana biləcəyimizi hesablayaq:
Tutaq ki, 10 diskdən 280 MB/san sürətlə oxuyuruq, yəni. 3 milyon dəfə 100 bayt. Ancaq xatırladığımız kimi, bizə lazım olan məlumat oxunandan 2600 dəfə azdır. Beləliklə, 3 milyonu 2600-ə bölüb alırıq Saniyədə 1100 qeyd.

Depressiv, elə deyilmi? Təbiət belədir Təsadüfi giriş HDD-də məlumatlara giriş - blok ölçüsündən asılı olmayaraq. Bu, təsadüfi girişin fiziki həddidir və heç bir verilənlər bazası belə şəraitdə daha çox sıxışdıra bilməz.

Bəs verilənlər bazası daha yüksək sürətə necə nail olur? Bu suala cavab vermək üçün aşağıdakı şəkildə baş verənlərə baxaq:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Burada görürük ki, ilk bir neçə dəqiqə üçün sürət həqiqətən saniyədə min rekorda bərabərdir. Bununla birlikdə, tələb olunandan daha çox oxunduğuna görə, məlumatlar əməliyyat sisteminin (linux) buff/keşində olur və sürət saniyədə daha layiqli 60 minə yüksəlir.

Beləliklə, daha sonra biz yalnız OS keşində olan və ya müqayisə edilə bilən giriş sürətinə malik SSD/NVMe saxlama cihazlarında yerləşən məlumatlara girişin sürətləndirilməsi ilə məşğul olacağıq.

Bizim vəziyyətimizdə, hər biri aşağıdakı kimi yüklənən 4 serverdən ibarət bir dəzgahda testlər keçirəcəyik:

CPU: Xeon E5-2680 v4 @ 2.40GHz 64 ip.
Yaddaş: 730 GB.
java versiyası: 1.8.0_111

Və burada əsas məqam cədvəllərdə oxunmalı olan məlumatların miqdarıdır. Fakt budur ki, siz tamamilə HBase keşinə yerləşdirilən cədvəldən məlumatları oxusanız, o zaman əməliyyat sisteminin buff/keşindən oxumağa belə gəlməyəcək. Çünki HBase standart olaraq yaddaşın 40%-ni BlockCache adlı struktura ayırır. Əsasən bu, ConcurrentHashMap-dır, burada açar fayl adı + blokun ofsetidir və dəyər bu ofsetdəki faktiki məlumatdır.

Beləliklə, yalnız bu strukturdan oxuyarkən biz əla sürət görürük, saniyədə bir milyon sorğu kimi. Ancaq təsəvvür edək ki, biz yüzlərlə gigabayt yaddaşı yalnız verilənlər bazası ehtiyacları üçün ayıra bilmərik, çünki bu serverlərdə işləyən bir çox başqa faydalı şeylər var.

Məsələn, bizim vəziyyətimizdə bir RS-də BlockCache həcmi təxminən 12 GB-dır. Bir node üzərində iki RS endik, yəni. Bütün qovşaqlarda BlockCache üçün 96 GB ayrılmışdır. Və dəfələrlə daha çox məlumat var, məsələn, FAST_DIFF ilə sıxılmış faylların ölçüsü 4 MB olan 130 cədvəl, hər biri 800 bölgə olsun, yəni. cəmi 410 GB (bu təmiz məlumatdır, yəni replikasiya faktorunu nəzərə almadan).

Beləliklə, BlockCache ümumi məlumat həcminin yalnız 23%-ni təşkil edir və bu, BigData adlanan şeyin real şərtlərinə daha yaxındır. Və əyləncə burada başlayır - çünki açıq-aydın, daha az önbellek hits, daha pis performans. Axı, darıxsanız, çox iş görməli olacaqsınız - yəni. sistem funksiyalarını çağırmağa enin. Bununla belə, bunun qarşısını almaq mümkün deyil, ona görə də gəlin tamamilə fərqli bir aspektə baxaq - keşdəki məlumatlara nə baş verir?

Vəziyyəti sadələşdirək və fərz edək ki, bizdə yalnız 1 obyektə uyğun bir keş var. Keşdən 3 dəfə böyük olan məlumat həcmi ilə işləməyə çalışdığımız zaman nələrin baş verəcəyinə dair bir nümunə, aşağıdakıları etməli olacağıq:

1. Blok 1-i keş yaddaşa yerləşdirin
2. Blok 1-i keşdən çıxarın
3. Blok 2-i keş yaddaşa yerləşdirin
4. Blok 2-i keşdən çıxarın
5. Blok 3-i keş yaddaşa yerləşdirin

5 hərəkət tamamlandı! Ancaq bu vəziyyəti normal adlandırmaq olmaz, əslində biz HBase-i bir dəstə tamamilə faydasız iş görməyə məcbur edirik. O, mütəmadi olaraq ƏS keşindən məlumatları oxuyur, onu BlockCache-ə yerləşdirir, ancaq məlumatların yeni bir hissəsi gəldiyi üçün onu dərhal atmaq üçün. Yazının əvvəlindəki animasiya problemin mahiyyətini göstərir - Zibil Kollektoru miqyasdan çıxır, atmosfer qızışır, uzaq və qaynar İsveçdə balaca Qreta əsəbləşir. Və biz İT adamları, uşaqların kədərlənməsini həqiqətən sevmirik, ona görə də bununla bağlı nə edə biləcəyimizi düşünməyə başlayırıq.

Keşin daşmaması üçün bütün blokları yox, onların yalnız müəyyən faizini yerləşdirsəniz necə olar? BlockCache-ə verilənləri yerləşdirmək üçün funksiyanın başlanğıcına sadəcə bir neçə sətir kod əlavə etməklə başlayaq:

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

Burada əsas məqam budur: ofset blokun fayldakı mövqeyidir və onun son rəqəmləri 00-dan 99-a qədər təsadüfi və bərabər paylanır. Ona görə də biz yalnız bizə lazım olan diapazona düşənləri atlayacağıq.

Məsələn, cacheDataBlockPercent = 20 təyin edin və nə baş verdiyinə baxın:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Nəticə göz qabağındadır. Aşağıdakı qrafiklərdə belə bir sürətlənmənin niyə baş verdiyi aydın olur - biz məlumatları dərhal Mars itlərinin boşaldılmasına atmaq üçün Sizifean işini görmədən çoxlu GC resurslarına qənaət edirik:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Eyni zamanda, CPU istifadəsi artır, lakin məhsuldarlıqdan çox azdır:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

BlockCache-də saxlanılan blokların fərqli olduğunu da qeyd etmək lazımdır. Əksəriyyəti, təxminən 95%-i məlumatların özüdür. Qalanı isə Bloom filtrləri və ya LEAF_INDEX və kimi metadatadır т.д.. Bu məlumat kifayət deyil, lakin çox faydalıdır, çünki məlumatlara birbaşa daxil olmamışdan əvvəl HBase burada daha çox axtarış aparmağın lazım olub-olmadığını və əgər varsa, maraq blokunun tam olaraq harada yerləşdiyini anlamaq üçün metaya müraciət edir.

Buna görə də kodda biz yoxlama şərtini görürük buf.getBlockType().isData() və bu meta sayəsində hər halda onu önbelleğe buraxacağıq.

İndi yükü artıraq və funksiyanı bir anda bir qədər sıxlaşdıraq. İlk sınaqda biz kəsilmə faizini = 20 etdik və BlockCache bir qədər az istifadə edildi. İndi onu 23%-ə təyin edək və doymanın hansı nöqtədə baş verdiyini görmək üçün hər 100 dəqiqədən bir 5 ip əlavə edək:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Burada görürük ki, orijinal versiya saniyədə təxminən 100 min sorğu ilə demək olar ki, dərhal tavanı vurur. Halbuki yamaq 300 minə qədər sürətlənmə verir. Eyni zamanda, aydındır ki, sonrakı sürətlənmə artıq o qədər də "pulsuz" deyil, CPU istifadəsi də artır.

Bununla belə, bu, çox zərif bir həll deyil, çünki blokların neçə faizinin önbelleğe alınması lazım olduğunu əvvəlcədən bilmirik, bu, yük profilindən asılıdır. Buna görə də oxu əməliyyatlarının aktivliyindən asılı olaraq bu parametrin avtomatik tənzimlənməsi mexanizmi tətbiq edilmişdir.

Buna nəzarət etmək üçün üç seçim əlavə edildi:

hbase.lru.cache.heavy.evaction.count.limit — optimallaşdırmadan istifadə etməyə başlamazdan əvvəl (yəni blokları atlamadan) əvvəl verilənlərin keşdən çıxarılması prosesinin neçə dəfə davam etməsini təyin edir. Varsayılan olaraq, MAX_INT = 2147483647-ə bərabərdir və əslində funksiyanın heç vaxt bu dəyərlə işləməyə başlamaması deməkdir. Çünki evakuasiya prosesi hər 5 - 10 saniyədən bir başlayır (bu yükdən asılıdır) və 2147483647 * 10/60/60/24/365 = 680 ildir. Bununla belə, biz bu parametri 0-a təyin edə və işə salındıqdan dərhal sonra funksiyanı işə sala bilərik.

Bununla belə, bu parametrdə faydalı yük də var. Əgər yükümüz elədirsə ki, qısamüddətli oxumalar (məsələn, gün ərzində) və uzunmüddətli oxumalar (gecələr) davamlı olaraq kəsişib gedirsə, o zaman funksiyanın yalnız uzun oxu əməliyyatları aparıldığı zaman işə salınmasına əmin ola bilərik.

Məsələn, biz bilirik ki, qısa müddətli oxunuşlar adətən təxminən 1 dəqiqə davam edir. Blokları atmağa başlamağa ehtiyac yoxdur, önbelleğin köhnəlməyə vaxtı olmayacaq və sonra biz bu parametri, məsələn, 10-a bərabər təyin edə bilərik. Bu, optimallaşdırmanın yalnız uzun müddətə işləyəcəyinə səbəb olacaq müddətli aktiv oxu başlamışdır, yəni. 100 saniyədə. Beləliklə, qısa müddətli oxumamız varsa, bütün bloklar önbelleğe girəcək və mövcud olacaq (standart alqoritmlə çıxarılacaqlar istisna olmaqla). Uzun müddətli oxuduqda funksiya işə salınır və biz daha yüksək performansa malik olarıq.

hbase.lru.cache.heavy.evaction.mb.size.limit — 10 saniyə ərzində önbelleğe neçə meqabayt yerləşdirmək istədiyimizi (və əlbəttə ki, çıxarmaq) təyin edir. Xüsusiyyət bu dəyərə çatmağa və onu saxlamağa çalışacaq. Məsələ burasındadır: əgər biz gigabaytları keş yaddaşa yığsaq, onda gigabaytları çıxarmalı olacağıq və bu, yuxarıda gördüyümüz kimi, çox baha başa gəlir. Bununla belə, onu çox kiçik təyin etməyə çalışmamalısınız, çünki bu, blok atlama rejiminin vaxtından əvvəl çıxmasına səbəb olacaq. Güclü serverlər üçün (təxminən 20-40 fiziki nüvə) təxminən 300-400 MB təyin etmək optimaldır. Orta sinif üçün (~10 nüvə) 200-300 MB. Zəif sistemlər üçün (2-5 nüvə) 50-100 MB normal ola bilər (bunlarda sınaqdan keçirilməyib).

Bunun necə işlədiyinə baxaq: deyək hbase.lru.cache.heavy.eviction.mb.size.limit = 500 təyin etdik, bir növ yük var (oxumaq) və sonra hər ~10 saniyədən bir neçə bayt olduğunu hesablayırıq. düsturla çıxarılır:

Yerüstü xərc = Sərbəst Bayt Məcmu (MB) * 100 / Limit (MB) - 100;

Əgər əslində 2000 MB çıxarılıbsa, o zaman Yerüstü xərc bərabərdir:

2000 * 100 / 500 - 100 = 300%

Alqoritmlər bir neçə on faizdən çox saxlamağa çalışır, buna görə də xüsusiyyət keşlənmiş blokların faizini azaldacaq və bununla da avtomatik tənzimləmə mexanizmini həyata keçirəcəkdir.

Bununla belə, yük azalarsa, deyək ki, yalnız 200 MB çıxarılır və Yerüstü yük mənfi olur (sözdə həddindən artıq yüklənmə):

200 * 100 / 500 - 100 = -60%

Əksinə, bu xüsusiyyət Overhead müsbət hala gələnə qədər keşlənmiş blokların faizini artıracaq.

Aşağıda bunun real məlumatlarda necə göründüyünə dair bir nümunə verilmişdir. 0%-ə çatmağa çalışmaq lazım deyil, mümkün deyil. Təxminən 30 - 100% olduqda çox yaxşıdır, bu, qısamüddətli dalğalanmalar zamanı optimallaşdırma rejimindən vaxtından əvvəl çıxmağın qarşısını almağa kömək edir.

hbase.lru.cache.heavy.evaction.overhead.əmsalı — nəticəni nə qədər tez əldə etmək istədiyimizi təyin edir. Oxumalarımızın əsasən uzun olduğunu və gözləmək istəmədiyini dəqiq bilsək, bu nisbəti artırıb yüksək performansı daha tez əldə edə bilərik.

Məsələn, bu əmsalı = 0.01 təyin etdik. Bu o deməkdir ki, üst-üstə düşmə (yuxarıya bax) nəticədə bu rəqəmə vurulacaq və keşlənmiş blokların faizi azalacaq. Fərz edək ki, Overhead = 300% və əmsal = 0.01, onda keşlənmiş blokların faizi 3% azalacaq.

Bənzər “Əks təzyiq” məntiqi mənfi Yerüstü (həddindən artıq) dəyərlər üçün də həyata keçirilir. Oxumaların və çıxarılmaların həcmində qısa müddətli dalğalanmalar həmişə mümkün olduğundan, bu mexanizm optimallaşdırma rejimindən vaxtından əvvəl çıxmağın qarşısını almağa imkan verir. Arxa təzyiqin tərsinə çevrilmiş məntiqi var: həddi aşmaq nə qədər güclü olarsa, bir o qədər çox blok keşlənir.

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

İcra kodu

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

İndi bütün bunlara real bir nümunə ilə baxaq. Aşağıdakı test skriptimiz var:

  1. Skanlamağa başlayaq (25 mövzu, toplu = 100)
  2. 5 dəqiqədən sonra çoxlu əldə əlavə edin (25 mövzu, toplu = 100)
  3. 5 dəqiqədən sonra multi-getləri söndürün (yalnız skan yenidən qalır)

Biz iki qaçış edirik, əvvəlcə hbase.lru.cache.heavy.eviction.count.limit = 10000 (bu, faktiki olaraq xüsusiyyəti söndürür), sonra isə limit = 0 (aktiv edir) təyin edirik.

Aşağıdakı qeydlərdə biz funksiyanın necə işə salındığını görürük və Overshooting-i 14-71%-ə sıfırlayır. Zaman zaman yük azalır, bu da Backpressure-i işə salır və HBase yenidən daha çox bloku önbelleğe alır.

RegionServerə daxil olun
çıxarıldı (MB): 0, nisbət 0.0, yerüstü (%): -100, ağır boşaltma sayğacı: 0, cari keşləmə DataBlock (%): 100
çıxarıldı (MB): 0, nisbət 0.0, yerüstü (%): -100, ağır boşaltma sayğacı: 0, cari keşləmə DataBlock (%): 100
çıxarıldı (MB): 2170, nisbət 1.09, yerüstü (%): 985, ağır boşaltma sayğacı: 1, cari keşləmə DataBlock (%): 91 < başlanğıc
çıxarıldı (MB): 3763, nisbət 1.08, yerüstü (%): 1781, ağır boşaltma sayğacı: 2, cari keşləmə DataBlock (%): 76
çıxarıldı (MB): 3306, nisbət 1.07, yerüstü (%): 1553, ağır boşaltma sayğacı: 3, cari keşləmə DataBlock (%): 61
çıxarıldı (MB): 2508, nisbət 1.06, yerüstü (%): 1154, ağır boşaltma sayğacı: 4, cari keşləmə DataBlock (%): 50
çıxarıldı (MB): 1824, nisbət 1.04, yerüstü (%): 812, ağır boşaltma sayğacı: 5, cari keşləmə DataBlock (%): 42
çıxarıldı (MB): 1482, nisbət 1.03, yerüstü (%): 641, ağır boşaltma sayğacı: 6, cari keşləmə DataBlock (%): 36
çıxarıldı (MB): 1140, nisbət 1.01, yerüstü (%): 470, ağır boşaltma sayğacı: 7, cari keşləmə DataBlock (%): 32
çıxarıldı (MB): 913, nisbət 1.0, yerüstü (%): 356, ağır boşaltma sayğacı: 8, cari keşləmə DataBlock (%): 29
çıxarıldı (MB): 912, nisbət 0.89, yerüstü (%): 356, ağır boşaltma sayğacı: 9, cari keşləmə DataBlock (%): 26
çıxarıldı (MB): 684, nisbət 0.76, yerüstü (%): 242, ağır boşaltma sayğacı: 10, cari keşləmə DataBlock (%): 24
çıxarıldı (MB): 684, nisbət 0.61, yerüstü (%): 242, ağır boşaltma sayğacı: 11, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 456, nisbət 0.51, yerüstü (%): 128, ağır boşaltma sayğacı: 12, cari keşləmə DataBlock (%): 21
çıxarıldı (MB): 456, nisbət 0.42, yerüstü (%): 128, ağır boşaltma sayğacı: 13, cari keşləmə DataBlock (%): 20
çıxarıldı (MB): 456, nisbət 0.33, yerüstü (%): 128, ağır boşaltma sayğacı: 14, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 15, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 342, nisbət 0.32, yerüstü (%): 71, ağır boşaltma sayğacı: 16, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 342, nisbət 0.31, yerüstü (%): 71, ağır boşaltma sayğacı: 17, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.3, yerüstü (%): 14, ağır boşaltma sayğacı: 18, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.29, yerüstü (%): 14, ağır boşaltma sayğacı: 19, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.27, yerüstü (%): 14, ağır boşaltma sayğacı: 20, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.25, yerüstü (%): 14, ağır boşaltma sayğacı: 21, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.24, yerüstü (%): 14, ağır boşaltma sayğacı: 22, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.22, yerüstü (%): 14, ağır boşaltma sayğacı: 23, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.21, yerüstü (%): 14, ağır boşaltma sayğacı: 24, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.2, yerüstü (%): 14, ağır boşaltma sayğacı: 25, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 228, nisbət 0.17, yerüstü (%): 14, ağır boşaltma sayğacı: 26, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 456, nisbət 0.17, yerüstü (%): 128, ağır boşaltma sayğacı: 27, cari keşləmə DataBlock (%): 18 < əlavə edilir (lakin cədvəl eynidir)
çıxarıldı (MB): 456, nisbət 0.15, yerüstü (%): 128, ağır boşaltma sayğacı: 28, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 342, nisbət 0.13, yerüstü (%): 71, ağır boşaltma sayğacı: 29, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 342, nisbət 0.11, yerüstü (%): 71, ağır boşaltma sayğacı: 30, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 342, nisbət 0.09, yerüstü (%): 71, ağır boşaltma sayğacı: 31, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 228, nisbət 0.08, yerüstü (%): 14, ağır boşaltma sayğacı: 32, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 228, nisbət 0.07, yerüstü (%): 14, ağır boşaltma sayğacı: 33, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 228, nisbət 0.06, yerüstü (%): 14, ağır boşaltma sayğacı: 34, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 228, nisbət 0.05, yerüstü (%): 14, ağır boşaltma sayğacı: 35, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 228, nisbət 0.05, yerüstü (%): 14, ağır boşaltma sayğacı: 36, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 228, nisbət 0.04, yerüstü (%): 14, ağır boşaltma sayğacı: 37, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 109, nisbət 0.04, yerüstü (%): -46, ağır boşaltma sayğacı: 37, cari keşləmə DataBlock (%): 22 < arxa təzyiq
çıxarıldı (MB): 798, nisbət 0.24, yerüstü (%): 299, ağır boşaltma sayğacı: 38, cari keşləmə DataBlock (%): 20
çıxarıldı (MB): 798, nisbət 0.29, yerüstü (%): 299, ağır boşaltma sayğacı: 39, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 570, nisbət 0.27, yerüstü (%): 185, ağır boşaltma sayğacı: 40, cari keşləmə DataBlock (%): 17
çıxarıldı (MB): 456, nisbət 0.22, yerüstü (%): 128, ağır boşaltma sayğacı: 41, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 342, nisbət 0.16, yerüstü (%): 71, ağır boşaltma sayğacı: 42, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 342, nisbət 0.11, yerüstü (%): 71, ağır boşaltma sayğacı: 43, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 228, nisbət 0.09, yerüstü (%): 14, ağır boşaltma sayğacı: 44, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 228, nisbət 0.07, yerüstü (%): 14, ağır boşaltma sayğacı: 45, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 228, nisbət 0.05, yerüstü (%): 14, ağır boşaltma sayğacı: 46, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 222, nisbət 0.04, yerüstü (%): 11, ağır boşaltma sayğacı: 47, cari keşləmə DataBlock (%): 16
çıxarıldı (MB): 104, nisbət 0.03, yerüstü (%): -48, ağır evakuasiya sayğacı: 47, cari keşləmə DataBlock (%): 21 < kəsir
çıxarıldı (MB): 684, nisbət 0.2, yerüstü (%): 242, ağır boşaltma sayğacı: 48, cari keşləmə DataBlock (%): 19
çıxarıldı (MB): 570, nisbət 0.23, yerüstü (%): 185, ağır boşaltma sayğacı: 49, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 342, nisbət 0.22, yerüstü (%): 71, ağır boşaltma sayğacı: 50, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 228, nisbət 0.21, yerüstü (%): 14, ağır boşaltma sayğacı: 51, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 228, nisbət 0.2, yerüstü (%): 14, ağır boşaltma sayğacı: 52, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 228, nisbət 0.18, yerüstü (%): 14, ağır boşaltma sayğacı: 53, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 228, nisbət 0.16, yerüstü (%): 14, ağır boşaltma sayğacı: 54, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 228, nisbət 0.14, yerüstü (%): 14, ağır boşaltma sayğacı: 55, cari keşləmə DataBlock (%): 18
çıxarıldı (MB): 112, nisbət 0.14, yerüstü (%): -44, ağır boşaltma sayğacı: 55, cari keşləmə DataBlock (%): 23 < arxa təzyiq
çıxarıldı (MB): 456, nisbət 0.26, yerüstü (%): 128, ağır boşaltma sayğacı: 56, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.31, yerüstü (%): 71, ağır boşaltma sayğacı: 57, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 58, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 59, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 60, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 61, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 62, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 63, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.32, yerüstü (%): 71, ağır boşaltma sayğacı: 64, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 65, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 66, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.32, yerüstü (%): 71, ağır boşaltma sayğacı: 67, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 68, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.32, yerüstü (%): 71, ağır boşaltma sayğacı: 69, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.32, yerüstü (%): 71, ağır boşaltma sayğacı: 70, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 71, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 72, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 73, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 74, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 75, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 342, nisbət 0.33, yerüstü (%): 71, ağır boşaltma sayğacı: 76, cari keşləmə DataBlock (%): 22
çıxarıldı (MB): 21, nisbət 0.33, yerüstü (%): -90, ağır boşaltma sayğacı: 76, cari keşləmə DataBlock (%): 32
çıxarıldı (MB): 0, nisbət 0.0, yerüstü (%): -100, ağır boşaltma sayğacı: 0, cari keşləmə DataBlock (%): 100
çıxarıldı (MB): 0, nisbət 0.0, yerüstü (%): -100, ağır boşaltma sayğacı: 0, cari keşləmə DataBlock (%): 100

Skanlar eyni prosesi iki keş bölməsi - tək (əvvəllər heç vaxt tələb olunmayan bloklar) və çoxlu (ən azı bir dəfə "tələb olunan" məlumatlar burada saxlanılır) arasındakı əlaqənin qrafiki şəklində göstərmək üçün lazım idi:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Və nəhayət, parametrlərin işləməsi qrafik şəklində necə görünür. Müqayisə üçün qeyd edək ki, keş başlanğıcda tamamilə söndürüldü, sonra HBase keşləmə ilə işə salındı ​​və optimallaşdırma işinin başlanmasını 5 dəqiqə (30 evakuasiya dövrü) gecikdirdi.

Tam kodu Pull Sorğuda tapa bilərsiniz HBASE 23887 github-da.

Bununla belə, saniyədə 300 min oxunuş bu şəraitdə bu aparatda əldə edilə bilənlərin hamısı deyil. Fakt budur ki, HDFS vasitəsilə məlumatlara daxil olmaq lazım olduqda, şəbəkə qarşılıqlı əlaqəsindən qaçaraq məlumatlara birbaşa daxil olmağa imkan verən ShortCircuitCache (bundan sonra SSC) mexanizmi istifadə olunur.

Profilinq göstərdi ki, bu mexanizm böyük qazanc versə də, o, həm də müəyyən bir nöqtədə darboğaza çevrilir, çünki demək olar ki, bütün ağır əməliyyatlar kilidin içərisində baş verir və bu, çox vaxt blokadaya səbəb olur.

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Bunu başa düşdükdən sonra başa düşdük ki, bir sıra müstəqil SSC-lər yaratmaqla problemdən yayınmaq olar:

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

Və sonra son ofset rəqəmindəki kəsişmələr istisna olmaqla, onlarla işləyin:

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

İndi test etməyə başlaya bilərsiniz. Bunun üçün HDFS-dən faylları sadə çox yivli proqramla oxuyacağıq. Parametrləri təyin edin:

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

Və sadəcə faylları oxuyun:

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

Bu kod ayrı-ayrı mövzularda icra olunur və biz eyni vaxtda oxunan faylların sayını (10-dan 200-ə qədər - üfüqi ox) və keşlərin sayını (1-dən 10-a qədər - qrafik) artıracağıq. Şaquli ox yalnız bir keşin olduğu vəziyyətə nisbətən SSC-nin artması nəticəsində yaranan sürətlənməni göstərir.

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Qrafiki necə oxumaq olar: Bir keş ilə 100 KB blokda 64 min oxunuş üçün icra müddəti 78 saniyə tələb edir. Halbuki 5 keş ilə 16 saniyə çəkir. Bunlar. ~5 dəfə sürətlənmə var. Qrafikdən göründüyü kimi az sayda paralel oxunuşda effekt o qədər də hiss olunmur, 50-dən çox oxunuş olduqda nəzərə çarpan rol oynamağa başlayır.O da nəzərə çarpır ki, SSC-lərin sayının 6-dan artırılması. və yuxarıda əhəmiyyətli dərəcədə kiçik performans artımı verir.

Qeyd 1: Test nəticələri olduqca dəyişkən olduğundan (aşağıya baxın), 3 qaçış aparıldı və nəticədə alınan dəyərlər orta hesabla götürüldü.

Qeyd 2: Girişin özü bir qədər yavaş olsa da, təsadüfi girişin konfiqurasiyası nəticəsində əldə edilən performans eynidir.

Bununla belə, aydınlaşdırmaq lazımdır ki, HBase ilə olan vəziyyətdən fərqli olaraq, bu sürətlənmə həmişə pulsuz olmur. Burada biz CPU-nun kilidlərə asılmaq əvəzinə daha çox iş görmək qabiliyyətini "açırıq".

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Burada müşahidə edə bilərsiniz ki, ümumiyyətlə, keşlərin sayının artması CPU istifadəsində təxminən mütənasib artım verir. Bununla belə, bir az daha çox qazanan birləşmələr var.

Məsələn, SSC = 3 parametrinə daha yaxından nəzər salaq. Diapazonda performans artımı təxminən 3.3 dəfədir. Aşağıda hər üç ayrı qaçışın nəticələri verilmişdir.

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

CPU istehlakı təxminən 2.8 dəfə artır. Fərq o qədər də böyük deyil, lakin balaca Qreta artıq xoşbəxtdir və məktəbə getməyə və dərs almaq üçün vaxt tapa bilər.

Beləliklə, bu, HDFS-ə toplu girişdən istifadə edən hər hansı alət üçün (məsələn, Spark və s.) müsbət təsir göstərəcək, bir şərtlə ki, tətbiq kodu yüngül olsun (yəni, fiş HDFS müştəri tərəfində olsun) və pulsuz CPU gücü olsun. . Yoxlamaq üçün gəlin HBase-dən oxumaq üçün BlockCache optimallaşdırmasının və SSC tənzimləməsinin birgə istifadəsinin hansı effekti verəcəyini yoxlayaq.

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Görünür ki, belə şəraitdə təsir zərif sınaqlarda olduğu kimi böyük deyil (heç bir emal olmadan oxumaq), lakin burada əlavə 80K sıxmaq olduqca mümkündür. Birlikdə, hər iki optimallaşdırma 4x sürətləndirməni təmin edir.

Bu optimallaşdırma üçün PR də hazırlanmışdır [HDFS-15202], birləşdirilmişdir və bu funksionallıq gələcək buraxılışlarda əlçatan olacaq.

Və nəhayət, oxşar geniş sütunlu verilənlər bazası olan Cassandra və HBase-nin oxu performansını müqayisə etmək maraqlı idi.

Bunu etmək üçün biz iki hostdan (cəmi 800 mövzu) standart YCSB yük testi yardım proqramının nümunələrini işə saldıq. Server tərəfində - 4 hostda RegionServer və Cassandra-nın 4 nümunəsi (təsirlərindən qaçmaq üçün müştərilərin işlədiyi yerlər deyil). Oxumalar ölçü cədvəllərindən gəldi:

HBase – HDFS-də 300 GB (100 GB təmiz məlumat)

Cassandra - 250 GB (replikasiya faktoru = 3)

Bunlar. həcm təxminən eyni idi (HBase-də bir az daha çox).

HBase parametrləri:

dfs.client.short.circuit.num = 5 (HDFS müştəri optimallaşdırması)

hbase.lru.cache.heavy.evaction.count.limit = 30 - bu o deməkdir ki, yamaq 30 boşalmadan sonra işə başlayacaq (~5 dəqiqə)

hbase.lru.cache.heavy.eviction.mb.size.limit = 300 — keşləmə və çıxarmanın hədəf həcmi

YCSB qeydləri təhlil edildi və Excel qrafiklərində tərtib edildi:

HBase-dən oxuma sürətini 3 dəfəyə qədər və HDFS-dən 5 dəfəyə qədər necə artırmaq olar

Gördüyünüz kimi, bu optimallaşdırmalar bu şərtlər altında bu verilənlər bazalarının işini müqayisə etməyə və saniyədə 450 min oxunuş əldə etməyə imkan verir.

Ümid edirik ki, bu məlumat məhsuldarlıq üçün maraqlı mübarizə zamanı kimsə üçün faydalı ola bilər.

Mənbə: www.habr.com

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