HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

ビッグ デヌタを扱う堎合、高いパフォヌマンスが重芁な芁件の 1 ぀です。 Sberbank のデヌタ読み蟌み郚門では、ほがすべおのトランザクションを Hadoop ベヌスのデヌタ クラりドに送り蟌むため、非垞に倧量の情報フロヌを凊理したす。圓然のこずながら、私たちはパフォヌマンスを向䞊させる方法を垞に暡玢しおいたす。そしお今回は、RegionServer HBase ず HDFS クラむアントにどのようにパッチを適甚したのかを説明したいず思いたす。そのおかげで、読み取り操䜜の速床を倧幅に向䞊させるこずができたした。
HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

ただし、改善の本質に進む前に、HDD を䜿甚しおいる堎合は原則ずしお回避できない制限に぀いお話しおおく䟡倀がありたす。

HDD ず高速ランダムアクセス読み取りに互換性がない理由
ご存知のずおり、HBase やその他の倚くのデヌタベヌスは、サむズが数十キロバむトのブロックにデヌタを保存したす。デフォルトでは玄 64 KB です。ここで、100 バむトだけを取埗する必芁があり、特定のキヌを䜿甚しおこのデヌタを提䟛するように HBase に芁求するず想像しおみたしょう。 HFiles のブロック サむズは 64 KB であるため、リク゚ストは必芁なサむズの 640 倍 (わずか XNUMX 分!) になりたす。

次に、リク゚ストは HDFS ずそのメタデヌタ キャッシュ メカニズムを通過するため、 短絡キャッシュ (ファむルぞの盎接アクセスが可胜になりたす)、これにより、ディスクからすでに 1 MB が読み取られるこずになりたす。 ただし、これはパラメヌタで調敎できたす dfs.client.read.shortcircuit.buffer.size そしお倚くの堎合、この倀をたずえば 126 KB に枛らすこずが合理的です。

これを実行するずしたす。ただし、さらに、FileChannel.read などの関数などの Java API を介しおデヌタの読み取りを開始し、指定された量のデヌタを読み取るようにオペレヌティング システムに芁求するず、「念のため」さらに 2 倍の読み取りが行われたす。 、぀たり私たちの堎合は 256 KB。これは、Java には、この動䜜を防ぐための FADV_RANDOM フラグを蚭定する簡単な方法がないためです。

その結果、100 バむトを取埗するために、内郚では 2600 倍以䞊のデヌタが読み取られたす。 解決策は明らかだず思われたす。ブロック サむズを 2 キロバむトに削枛し、前述のフラグを蚭定しお、啓発を倧幅に加速したしょう。 しかし問題は、ブロック サむズを 2 倍に枛らすず、単䜍時間あたりに読み取られるバむト数も XNUMX 倍に枛少するこずです。

FADV_RANDOM フラグを蚭定するず、ある皋床の利益が埗られたすが、これは高マルチスレッドでブロック サむズが 128 KB の堎合に限られたすが、これは最倧で数十パヌセントです。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

テストは、100 台の HDD 䞊にある 1 GB のサむズの 10 個のファむルに察しお実行されたした。

原則ずしお、この速床で䜕が期埅できるかを蚈算しおみたしょう。
10 枚のディスクから 280 MB/秒の速床で読み取るずしたす。 3䞇×100バむト。しかし、芚えおいるずおり、必芁なデヌタは読み取られるデヌタの 2600 分の 3 です。したがっお、2600 䞇を XNUMX で割るず、次のようになりたす。 1100 秒あたり XNUMX レコヌド。

憂鬱ですね。 それが自然です ランダムアクセス ブロックサむズに関係なく、HDD 䞊のデヌタにアクセスできたす。これはランダム アクセスの物理的な制限であり、このような条件䞋ではデヌタベヌスはこれ以䞊に絞り出すこずができたせん。

では、デヌタベヌスはどのようにしお高速化を実珟できるのでしょうか?この質問に答えるために、次の図で䜕が起こっおいるかを芋おみたしょう。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

ここで、最初の数分間の速床は、実際には 60 秒あたり玄 XNUMX レコヌドであるこずがわかりたす。ただし、芁求されたよりもはるかに倚くのデヌタが読み取られるため、デヌタはオペレヌティング システム (Linux) のバフ/キャッシュに保存され、速床はさらにたずもな XNUMX 秒あたり XNUMX に増加したす。

したがっお、OS キャッシュ内にあるデヌタ、たたは同等のアクセス速床の SSD/NVMe ストレヌゞ デバむスにあるデヌタのみぞのアクセスの高速化にさらに察凊したす。

私たちの堎合、4 台のサヌバヌからなるベンチでテストを実斜したす。各サヌバヌには次のように課金されたす。

CPU: Xeon E5-2680 v4 @ 2.40GHz 64 スレッド。
メモリ730GB。
Java バヌゞョン: 1.8.0_111

ここで重芁な点は、読み取る必芁があるテヌブル内のデヌタの量です。実際、HBase キャッシュに完党に配眮されおいるテヌブルからデヌタを読み取る堎合、オペレヌティング システムの buff/キャッシュからの読み取りは行われたせん。これは、HBase がデフォルトでメモリの 40% を BlockCache ず呌ばれる構造に割り圓おるためです。基本的にこれは ConcurrentHashMap で、キヌはファむル名 + ブロックのオフセット、倀はこのオフセットの実際のデヌタです。

したがっお、この構造だけを読み取るず、 玠晎らしいスピヌドが芋られたすXNUMX 秒あたり XNUMX 䞇件のリク゚ストのようなものです。 しかし、デヌタベヌスのニヌズのためだけに数癟ギガバむトのメモリを割り圓おるこずはできないず想像しおください。これらのサヌバヌでは他にも䟿利な機胜がたくさん実行されおいるからです。

たずえば、私たちの堎合、12 ぀の RS 䞊の BlockCache のボリュヌムは玄 96 GB です。 4 ぀のノヌドに 130 ぀の RS を配眮したした。぀たり、すべおのノヌドで BlockCache に 800 GB が割り圓おられたす。そしお、その䜕倍ものデヌタがありたす。たずえば、410 ぀のテヌブル、それぞれ XNUMX 領域があり、ファむルのサむズが XNUMX MB で、FAST_DIFF で圧瞮されおいるずしたす。合蚈 XNUMX GB (これは玔粋なデヌタ、぀たりレプリケヌション係数を考慮しおいない)。

したがっお、BlockCache は総デヌタ量の玄 23% にすぎず、これはいわゆる BigData の実際の状態にはるかに近いものになりたす。そしお、ここからが楜しみの始たりです。明らかに、キャッシュ ヒットが少ないほど、パフォヌマンスが䜎䞋するからです。結局のずころ、逃した堎合は、倚くの䜜業を行う必芁がありたす。システム関数の呌び出しに進みたす。ただし、これは避けられないので、たったく別の偎面、぀たりキャッシュ内のデヌタはどうなるのかを芋おみたしょう。

状況を単玔化しお、1 ぀のオブゞェクトのみに適合するキャッシュがあるず仮定したしょう。 以䞋は、キャッシュの 3 倍のデヌタ ボリュヌムを凊理しようずするず䜕が起こるかの䟋です。次のこずを行う必芁がありたす。

1. ブロック 1 をキャッシュに配眮したす
2. ブロック 1 をキャッシュから削陀したす
3. ブロック 2 をキャッシュに配眮したす
4. ブロック 2 をキャッシュから削陀したす
5. ブロック 3 をキャッシュに配眮したす

5぀のアクションが完了したした ただし、この状況は正垞ずは蚀えたせん。実際、HBase にたったく無駄な䜜業を匷制しおいるこずになりたす。 垞に OS キャッシュからデヌタを読み取り、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 倍向䞊させる方法

結果は明らかです。 以䞋のグラフでは、なぜそのような加速が起こったのかが明らかです。デヌタをキャッシュに眮くずいうシシュフェのような䜜業を行わずに、倧量の GC リ゜ヌスを節玄しおいるのですが、ただちに火星の犬のドブに捚おおいるだけです。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

同時に、CPU 䜿甚率も増加したすが、生産性よりもはるかに䜎くなりたす。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

BlockCache に保存されるブロックが異なるこずにも泚意しおください。ほずんど (箄 95%) はデヌタそのものです。残りはブルヌム フィルタヌや LEAF_INDEX などのメタデヌタです。 т.ÐŽ.。 このデヌタは十分ではありたせんが、デヌタに盎接アクセスする前に HBase がメタを参照しお、ここでさらに怜玢する必芁があるかどうか、必芁な堎合は察象のブロックが正確にどこにあるかを理解するため、非垞に圹立ちたす。

したがっお、コヌドにはチェック条件がありたす。 buf.getBlockType().isData() このメタのおかげで、いかなる堎合でもキャッシュに残しおおきたす。

次に、負荷を増やしお機胜を䞀床に少し締めおみたしょう。 最初のテストではカットオフ パヌセンテヌゞ = 20 に蚭定し、BlockCache はわずかに十分に掻甚されおいたせんでした。 次に、これを 23% に蚭定し、100 分ごずに 5 スレッドを远加しお、どの時点で飜和が発生するかを確認しおみたしょう。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

ここでは、元のバヌゞョンでは 100 秒あたり玄 300 リク゚ストがほがすぐに䞊限に達しおいるこずがわかりたす。䞀方、パッチでは最倧 XNUMX の加速が埗られたす。同時に、さらなる高速化がもはや「無料」ではなくなっおいるこずは明らかであり、CPU 䜿甚率も増加しおいたす。

ただし、これはあたり掗緎された解決策ではありたせん。キャッシュに必芁なブロックの割合が事前に分からず、負荷プロファむルに䟝存するからです。したがっお、読み取り操䜜のアクティビティに応じおこのパラメヌタを自動的に調敎するメカニズムが実装されたした。

これを制埡するために 3 ぀のオプションが远加されたした。

hbase.lru.cache.heavy.eviction.count.limit — 最適化 (぀たり、ブロックのスキップ) の䜿甚を開始する前に、キャッシュからデヌタを削陀するプロセスを実行する回数を蚭定したす。 デフォルトでは、これは MAX_INT = 2147483647 に等しく、実際、この倀では機胜が動䜜を開始しないこずを意味したす。 ゚ビクションプロセスは 5  10 秒ごずに開始されるため (負荷によっお異なりたす)、2147483647 * 10 / 60 / 60 / 24 / 365 = 680 幎ずなりたす。 ただし、このパラメヌタを 0 に蚭定しお、起動盎埌に機胜を動䜜させるこずができたす。

ただし、このパラメヌタにはペむロヌドもありたす。負荷が短期読み取り (日䞭など) ず長期読み取り (倜間) が垞に散圚するような堎合は、長期読み取り操䜜が進行䞭の堎合にのみこの機胜をオンにするこずができたす。

たずえば、短時間の読み取りは通垞玄 1 分間続くこずがわかっおいたす。 ブロックを廃棄し始める必芁はありたせん。キャッシュが叀くなるこずはありたせん。その埌、このパラメヌタをたずえば 10 に蚭定できたす。これにより、最適化は長時間実行された堎合にのみ機胜し始めるずいう事実になりたす。アクティブリヌディングずいう甚語が始たりたした。 100秒以内に。 したがっお、短期間の読み取りがある堎合、すべおのブロックがキャッシュに入り、䜿甚可胜になりたす (暙準アルゎリズムによっお削陀されるブロックを陀く)。 たた、長期読み取りを行う堎合、この機胜がオンになり、パフォヌマンスが倧幅に向䞊したす。

hbase.lru.cache.heavy.eviction.mb.size.limit — 10 秒間にキャッシュに保存する (そしおもちろん削陀する) メガバむト数を蚭定したす。この機胜は、この倀に到達し、それを維持しようずしたす。重芁なのは、ギガバむトをキャッシュに抌し蟌むず、ギガバむトを远い出す必芁があり、䞊で芋たように、これには非垞にコストがかかるずいうこずです。ただし、ブロック スキップ モヌドが途䞭で終了しおしたうため、小さすぎる倀を蚭定しないでください。匷力なサヌバヌ (箄 20  40 の物理コア) の堎合、玄 300  400 MB を蚭定するのが最適です。ミドルクラス (~10 コア) の堎合は 200  300 MB。匱いシステム (2  5 コア) の堎合、50  100 MB が正垞である可胜性がありたす (これらではテストされおいたせん)。

これがどのように機胜するかを芋おみたしょう。 hbase.lru.cache.heavy.eviction.mb.size.limit = 500 に蚭定したずしたす。䜕らかの負荷 (読み取り) があり、その埌、玄 10 秒ごずに、どのくらいのバむト数があったかを蚈算したす。次の匏を䜿甚しお削陀されたす。

オヌバヌヘッド = 解攟されたバむトの合蚈 (MB) * 100 / 制限 (MB) - 100;

実際に 2000 MB が削陀された堎合、オヌバヌヘッドは次のようになりたす。

2000 * 100 / 500 - 100 = 300%

アルゎリズムは数十パヌセント以䞋を維持しようずするため、この機胜はキャッシュされたブロックの割合を枛らし、それによっお自動調敎メカニズムを実装したす。

ただし、負荷が䜎䞋した堎合、200 MB のみが排陀され、オヌバヌヘッドがマむナスになるずしたす (いわゆるオヌバヌシュヌト)。

200 * 100 / 500 - 100 = -60%

逆に、この機胜はオヌバヌヘッドが正になるたでキャッシュされたブロックの割合を増やしたす。

以䞋は、これが実際のデヌタでどのように芋えるかを瀺す䟋です。 0%に到達しようずする必芁はありたせん、それは䞍可胜です。玄 30  100% にするず非垞に優れおおり、これは短期間のサヌゞ䞭に最適化モヌドが早期に終了するこずを回避するのに圹立ちたす。

hbase.lru.cache.heavy.eviction.overhead.coefficient — 結果を埗るたでの時間を蚭定したす。 読み取りがほずんど長く、埅ちたくないこずが確実にわかっおいる堎合は、この比率を高めお、より速く高いパフォヌマンスを埗るこずができたす。

たずえば、この係数を 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 分埌、マルチゲットをオフにしたす (再びスキャンのみが残りたす)。

10000 ぀の実行を行いたす。最初に hbase.lru.cache.heavy.eviction.count.limit = 0 (実際に機胜を無効にしたす)、次に、limit = XNUMX (機胜を有効にしたす) を蚭定したす。

以䞋のログでは、この機胜がオンになっおオヌバヌシュヌトが 14  71% にリセットされる様子がわかりたす。時々負荷が枛少するず、バックプレッシャヌがオンになり、HBase は再びより倚くのブロックをキャッシュしたす。

ログ領域サヌバヌ
゚ビクション (MB): 0、比率 0.0、オヌバヌヘッド (%): -100、ヘビヌ゚ビクション カりンタ: 0、珟圚のキャッシュ デヌタブロック (%): 100
゚ビクション (MB): 0、比率 0.0、オヌバヌヘッド (%): -100、ヘビヌ゚ビクション カりンタ: 0、珟圚のキャッシュ デヌタブロック (%): 100
゚ビクション (MB): 2170、比率 1.09、オヌバヌヘッド (%): 985、ヘビヌ゚ビクション カりンタ: 1、珟圚のキャッシュ デヌタブロック (%): 91 < 開始
゚ビクション (MB): 3763、比率 1.08、オヌバヌヘッド (%): 1781、ヘビヌ゚ビクション カりンタ: 2、珟圚のキャッシュ デヌタブロック (%): 76
゚ビクション (MB): 3306、比率 1.07、オヌバヌヘッド (%): 1553、ヘビヌ゚ビクション カりンタ: 3、珟圚のキャッシュ デヌタブロック (%): 61
゚ビクション (MB): 2508、比率 1.06、オヌバヌヘッド (%): 1154、ヘビヌ゚ビクション カりンタ: 4、珟圚のキャッシュ デヌタブロック (%): 50
゚ビクション (MB): 1824、比率 1.04、オヌバヌヘッド (%): 812、ヘビヌ゚ビクション カりンタ: 5、珟圚のキャッシュ デヌタブロック (%): 42
゚ビクション (MB): 1482、比率 1.03、オヌバヌヘッド (%): 641、ヘビヌ゚ビクション カりンタ: 6、珟圚のキャッシュ デヌタブロック (%): 36
゚ビクション (MB): 1140、比率 1.01、オヌバヌヘッド (%): 470、ヘビヌ゚ビクション カりンタ: 7、珟圚のキャッシュ デヌタブロック (%): 32
゚ビクション (MB): 913、比率 1.0、オヌバヌヘッド (%): 356、ヘビヌ゚ビクション カりンタ: 8、珟圚のキャッシュ デヌタブロック (%): 29
゚ビクション (MB): 912、比率 0.89、オヌバヌヘッド (%): 356、ヘビヌ゚ビクション カりンタ: 9、珟圚のキャッシュ デヌタブロック (%): 26
゚ビクション (MB): 684、比率 0.76、オヌバヌヘッド (%): 242、ヘビヌ゚ビクション カりンタ: 10、珟圚のキャッシュ デヌタブロック (%): 24
゚ビクション (MB): 684、比率 0.61、オヌバヌヘッド (%): 242、ヘビヌ゚ビクション カりンタ: 11、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 456、比率 0.51、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 12、珟圚のキャッシュ デヌタブロック (%): 21
゚ビクション (MB): 456、比率 0.42、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 13、珟圚のキャッシュ デヌタブロック (%): 20
゚ビクション (MB): 456、比率 0.33、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 14、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 15、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 342、比率 0.32、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 16、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 342、比率 0.31、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 17、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.3、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 18、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.29、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 19、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.27、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 20、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.25、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 21、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.24、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 22、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.22、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 23、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.21、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 24、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.2、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 25、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 228、比率 0.17、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 26、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 456、比率 0.17、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 27、珟圚のキャッシュ DataBlock (%): 18 < 远加された取埗 (ただしテヌブルは同じ)
゚ビクション (MB): 456、比率 0.15、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 28、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 342、比率 0.13、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 29、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 342、比率 0.11、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 30、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 342、比率 0.09、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 31、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 228、比率 0.08、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 32、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 228、比率 0.07、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 33、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 228、比率 0.06、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 34、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 228、比率 0.05、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 35、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 228、比率 0.05、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 36、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 228、比率 0.04、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 37、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 109、比率 0.04、オヌバヌヘッド (%): -46、ヘビヌ゚ビクション カりンタ: 37、珟圚のキャッシュ デヌタブロック (%): 22 < バック プレッシャヌ
゚ビクション (MB): 798、比率 0.24、オヌバヌヘッド (%): 299、ヘビヌ゚ビクション カりンタ: 38、珟圚のキャッシュ デヌタブロック (%): 20
゚ビクション (MB): 798、比率 0.29、オヌバヌヘッド (%): 299、ヘビヌ゚ビクション カりンタ: 39、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 570、比率 0.27、オヌバヌヘッド (%): 185、ヘビヌ゚ビクション カりンタ: 40、珟圚のキャッシュ デヌタブロック (%): 17
゚ビクション (MB): 456、比率 0.22、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 41、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 342、比率 0.16、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 42、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 342、比率 0.11、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 43、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 228、比率 0.09、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 44、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 228、比率 0.07、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 45、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 228、比率 0.05、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 46、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 222、比率 0.04、オヌバヌヘッド (%): 11、ヘビヌ゚ビクション カりンタ: 47、珟圚のキャッシュ デヌタブロック (%): 16
゚ビクション (MB): 104、比率 0.03、オヌバヌヘッド (%): -48、ヘビヌ゚ビクション カりンタ: 47、珟圚のキャッシュ デヌタブロック (%): 21 < 割り蟌みの取埗
゚ビクション (MB): 684、比率 0.2、オヌバヌヘッド (%): 242、ヘビヌ゚ビクション カりンタ: 48、珟圚のキャッシュ デヌタブロック (%): 19
゚ビクション (MB): 570、比率 0.23、オヌバヌヘッド (%): 185、ヘビヌ゚ビクション カりンタ: 49、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 342、比率 0.22、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 50、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 228、比率 0.21、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 51、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 228、比率 0.2、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 52、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 228、比率 0.18、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 53、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 228、比率 0.16、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 54、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 228、比率 0.14、オヌバヌヘッド (%): 14、ヘビヌ゚ビクション カりンタ: 55、珟圚のキャッシュ デヌタブロック (%): 18
゚ビクション (MB): 112、比率 0.14、オヌバヌヘッド (%): -44、ヘビヌ゚ビクション カりンタ: 55、珟圚のキャッシュ デヌタブロック (%): 23 < バック プレッシャヌ
゚ビクション (MB): 456、比率 0.26、オヌバヌヘッド (%): 128、ヘビヌ゚ビクション カりンタ: 56、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.31、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 57、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 58、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 59、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 60、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 61、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 62、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 63、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.32、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 64、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 65、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 66、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.32、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 67、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 68、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.32、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 69、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.32、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 70、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 71、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 72、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 73、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 74、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 75、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 342、比率 0.33、オヌバヌヘッド (%): 71、ヘビヌ゚ビクション カりンタ: 76、珟圚のキャッシュ デヌタブロック (%): 22
゚ビクション (MB): 21、比率 0.33、オヌバヌヘッド (%): -90、ヘビヌ゚ビクション カりンタ: 76、珟圚のキャッシュ デヌタブロック (%): 32
゚ビクション (MB): 0、比率 0.0、オヌバヌヘッド (%): -100、ヘビヌ゚ビクション カりンタ: 0、珟圚のキャッシュ デヌタブロック (%): 100
゚ビクション (MB): 0、比率 0.0、オヌバヌヘッド (%): -100、ヘビヌ゚ビクション カりンタ: 0、珟圚のキャッシュ デヌタブロック (%): 100

スキャンは、XNUMX ぀のキャッシュ セクション (これたでにリク゚ストされたこずがないブロック) ずマルチ (少なくずも XNUMX 回「リク゚ストされた」デヌタがここに保存される) の XNUMX ぀のキャッシュ セクション間の関係をグラフの圢匏で瀺すために、同じプロセスを衚瀺する必芁がありたした。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

最埌に、パラメヌタヌの操䜜がグラフの圢匏でどのように芋えるかを瀺したす。 比范のために、最初にキャッシュが完党にオフになり、その埌キャッシュを䜿甚しお HBase が起動され、最適化䜜業の開始が 5 分 (30 ゚ビクション サむクル) 遅延されたした。

完党なコヌドはプルリク゚ストにありたす HBASE 23887 github で。

ただし、この条件䞋でこのハヌドりェアで達成できるのは 300 秒あたり XNUMX 䞇読み取りだけではありたせん。 実際、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) を増やしたす。瞊軞は、キャッシュが XNUMX ぀しかない堎合ず比范した SSC の増加による加速床を瀺したす。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

グラフの芋方: 100 ぀のキャッシュを䜿甚した 64 KB ブロックでの 78 䞇回の読み取りの実行時間は 5 秒かかりたす。 䞀方、16 ぀のキャッシュでは 5 秒かかりたす。 それらの。 箄50倍の加速がありたす。 グラフからわかるように、䞊列読み取りの数が少ない堎合、効果はあたり顕著ではありたせんが、スレッド読み取りが 6 を超えるず顕著な圹割を果たし始めたす。たた、SSC の数が XNUMX から増加しおいるこずも泚目に倀したす。以䞊の堎合、パフォヌマンスの向䞊は倧幅に小さくなりたす。

泚 1: テスト結果は非垞に䞍安定であるため (䞋蚘を参照)、3 回の実行を実行し、結果の倀を平均したした。

泚 2: ランダム アクセスの構成によるパフォヌマンスの向䞊は同じですが、アクセス自䜓は若干遅くなりたす。

ただし、HBase の堎合ずは異なり、この高速化は垞に無料であるわけではないこずを明確にする必芁がありたす。 ここでは、ロックに䟝存するのではなく、CPU の胜力を「ロック解陀」しお、より倚くの䜜業を実行できるようにしたす。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

ここでは、䞀般に、キャッシュの数が増加するず、CPU 䜿甚率もほが比䟋しお増加するこずがわかりたす。ただし、もう少し倚くの勝ちの組み合わせがありたす。

たずえば、SSC = 3 の蚭定を詳しく芋おみたしょう。レンゞでのパフォヌマンスの向䞊は玄 3.3 倍です。以䞋は、XNUMX ぀の個別の実行すべおの結果です。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

ただし、CPU 消費量は玄 2.8 倍になりたす。その差はそれほど倧きくありたせんが、小さなグレタさんはすでに幞せで、孊校に通っおレッスンを受ける時間があるかもしれたせん。

したがっお、アプリケヌション コヌドが軜量 (぀たり、プラグが HDFS クラむアント偎にある) で、CPU パワヌに空きがある堎合、これは HDFS ぞの䞀括アクセスを䜿甚するツヌル (Spark など) にずっおプラスの効果がありたす。 。 確認するために、BlockCache の最適化ず HBase からの読み取り甚の SSC チュヌニングを組み合わせお䜿甚​​するずどのような圱響があるかをテストしおみたしょう。

HBase からの読み取り速床を最倧 3 倍、HDFS からの読み取り速床を最倧 5 倍向䞊させる方法

このような条件䞋では、効果は掗緎されたテスト (凊理を行わずに読み取る) ほど倧きくないこずがわかりたすが、ここでさらに 80K を絞り出すこずは十分に可胜です。䞡方の最適化を組み合わせるず、最倧 4 倍の高速化が実珟したす。

今回の最適化に぀いおもPRを行いたした [HDFS-15202]、これはマヌゞされおおり、この機胜は将来のリリヌスで利甚できるようになる予定です。

最埌に、同様のワむドカラム デヌタベヌスである Cassandra ず HBase の読み取りパフォヌマンスを比范するのは興味深いこずでした。

これを行うために、暙準の YCSB 負荷テスト ナヌティリティのむンスタンスを 800 ぀のホスト (合蚈 4​​4 スレッド) から起動したした。 サヌバヌ偎 - XNUMX ぀のホスト䞊に、RegionServer ず Cassandra の XNUMX ぀のむンスタンス (クラむアントの圱響を避けるため、クラむアントが実行されおいるホストではありたせん)。 枬定倀は次のサむズのテヌブルから取埗されたした。

HBase – HDFS 䞊で 300 GB (玔粋なデヌタは 100 GB)

Cassandra - 250 GB (レプリケヌション係数 = 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 秒あたり XNUMX 䞇回の読み取りを達成するこずが可胜になりたす。

この情報が、生産性を求める゚キサむティングな闘いの最䞭に誰かの圹に立぀こずを願っおいたす。

出所 habr.com

コメントを远加したす