4

Cloudera Manager パーセルを通じてインストールされた、CDH5.0.2 を実行する 4 つのデータノード クラスターがあります。1,300 万人のユーザーの行を HBase にインポートするために、単純な Python スクリプトを作成し、hadoop ストリーミング jar を使用しました。10万行までは期待どおりに機能します。そして...そして、次々と、すべてのデータノードが同じメッセージでクラッシュします:

The health test result for REGION_SERVER_GC_DURATION  has become bad: 
Average time spent in garbage collection was 44.8 second(s) (74.60%) 
per minute over the previous 5 minute(s). 
Critical threshold: 60.00%.

Web で見られるアドバイス ( [1][2][3]など) に従って問題を解決しようとしても、解決には至りません。Java ヒープ サイズで「遊んで」も無駄です。状況を「解決」した唯一のことは、リージョン サーバーのガベージ コレクション期間の監視期間を 5 秒から 50 秒に増やしたことです。間違いなく汚い回避策。

現在、GC 使用状況のモニターを作成する人員がいません。最終的にはそうなりますが、1,300 万行を HBase にインポートすると、すべてのリージョン サーバーが確実にクラッシュする可能性があるのではないかと考えていました。きれいな解決策はありますか?

編集:

データノードの JVM オプションは次のとおりです。

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:-CMSConcurrentMTEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled

データノードは、CentOS 6.5 を実行する物理マシンで、それぞれ 32Gb RAM と 2GHz の 1Quadcore と 30Mb キャッシュを備えています。

以下は、実行する Python スクリプトの抜粋です。2 つのテーブルに入力します。1 つは一意のユーザー ID を行キーとして、1 つの列ファミリにはユーザー情報を、もう 1 つはアクセスしたいすべての情報を行キーとして格納します。

#!/usr/bin/env python2.7
import sys
import happybase
import json
connection = happybase.Connection(host=master_ip)
hbase_main_table = connection.table('users_table')
hbase_index_table = connection.table('users_index_table')
header = ['ID', 'COL1', 'COL2', 'COL3', 'COL4']
for line in sys.stdin:
    l = line.replace('"','').strip("\n").split("\t")
    if l[header.index("ID")] == "ID":
        #you are reading the header
        continue
    for h in header[1:]:
        try:
            id = str(l[header.index("ID")])
            col = 'info:' + h.lower()
            val = l[header.index(h)].strip()
            hbase_table.put(id_au_bytes, {
                    col: val
                    })
            indexed = ['COL3', 'COL4']
            for typ in indexed:
               idx = l[header.index(typ)].strip()
               if len(idx) == 0:
                   continue
               row = hbase_index_table.row(idx)
               old_ids = row.get('d:s')
               if old_ids is not None:
                   ids = json.dumps(list(set(json.loads(old_ids)).union([id_au])))
               else:
                   ids = json.dumps([id_au])
               hbase_index.put(idx, {
                       'd:s': ids,
                       'd:t': typ,
                       'd:b': 'ame'
                       })
       except:
           msg = 'ERROR '+str(l[header.index("ID")])
           logging.info(msg, exc_info=True)
4

1 に答える 1

3

最近多くの人が直面している主要な問題の 1 つは、Java アプリケーションで使用できる RAM の量が爆発的に増加したことですが、Java GC のチューニングに関する情報のほとんどは、32 ビット時代の経験に基づいています。

私は最近、恐ろしい「長い一時停止」を避けるために、ヒープが大きい状況での GC の調査にかなりの時間を費やしました。私はこの素晴らしいプレゼンテーションを何度か見ましたが、ついに GC と私が直面した問題がより理解できるようになりました。

私は Hadoop についてあまり詳しくありませんが、若い世代が小さすぎるという状況に陥っている可能性があると思います。残念ながら、JVM GC チューニングに関するほとんどの情報では、オブジェクトを GC するのに最適な場所は若い世代であるということを強調できていません。文字通り時間がかからないこの時点でゴミを収集します。詳細については触れませんが (知りたい場合はプレゼンテーションをご覧ください)、若い (新しい) 世代に十分なスペースがないと、時期尚早に埋まってしまいます。これによりコレクションが強制され、一部のオブジェクトは旧世代に移動されます。最終的には、Tenured 世代がいっぱいになり、それも収集する必要があります。Tenured 世代に大量のガベージがある場合、Tenured コレクション アルゴリズムは一般に、ガベージ コレクションの時間がゼロではないマーク スイープであるため、非常に遅くなる可能性があります。

ホットスポットを使用していると思います。これは、ホットスポットのさまざまな GC 引数の良いリファレンスです。JVM GC オプション

若い世代の規模を大幅に拡大することから始めます。ここでの私の仮定は、短命から中命のオブジェクトがたくさん作成されているということです。あなたが避けたいのは、これらを終身世代に昇格させることです。その方法は、彼らが若い世代で過ごす時間を延長することです。これを達成するには、サイズを大きくする (いっぱいになるまでに時間がかかるようにする) か、保有期間のしきい値 (基本的にオブジェクトが保持される若いコレクションの数) を大きくすることができます。Tenuring しきい値の問題は、若い世代でオブジェクトを移動するのに時間がかかることです。若い世代のサイズを大きくすることは、メモリの面で非効率的ですが、私の推測では、多くの余裕があると思います。

私はキャッシュ サーバーでこのソリューションを使用しました。100 ミリ秒を超える範囲のマイナー コレクションと、一般的に 0.5 秒未満の (1 日に 1 つ未満の) メジャー コレクションがあり、ヒープは約 4GB です。オブジェクトは 5 分、15 分、または 29 日間存続します。

考慮すべきもう 1 つのことは、HotSpot に最近 (比較的言えば) 追加された G1 (ガベージ ファースト) コレクターです。

このアドバイスがあなたにどれだけ役立つか興味があります。幸運を。

于 2015-01-16T16:12:33.393 に答える