Hadoop クラスターで辞書を作成しており、トークンごとに数値ID を生成する必要があります。どうすればいいですか?
3 に答える
2つの問題があります。まず、トークンごとに1つのIDを割り当てるようにします。これを行うには、レコードをトークンで並べ替えてグループ化し、レデューサーで割り当てを行う必要があります。レデューサーメソッドがトークンごとに1回だけ呼び出されることを確認したら、コンテキストからのパーティション番号と、レデューサーによって維持される一意の数値ID(パーティションごとに1つのインスタンス)を使用できます。1に初期化されたインスタンス変数を使用するだけです。 setupメソッドで、reduceメソッドでインクリメントされます。
ビジネスロジックの一部ではない同期、並べ替え、およびグループ化を回避するには、いくつかのトリックを使用できます。これにより高速になります。
最も簡単な方法は、キーUUID UUID.randomUUID()ごとに 1 つずつ、Reducer で UUID を生成することですが、これらは数値ではありません。
数値 ID の連続したシーケンスが必要で、出力が 1 つのレデューサーで処理するのに十分小さい場合は、org.apache.hadoop.mapreduce.Job.setNumReduceTasks(int tasks) を介してジョブに 1 つのレデューサーを強制するよりも、すべてのキーが単一の Reducer に向けられます。
Mapper の出力がまだ単一の Reducer には大きすぎて、ID のシーケンスの連続性を気にしない場合、または辞書を分割できる場合は、Partitioner でいくつかのトリックを使用できます ( 「 」を参照)。キーを既知の長さの N 個の範囲に論理的に分割できるという考え方です (例: 範囲 1 は 1 で始まる 1 ミルのキーを持つことができ、範囲 4 は 3500000 で始まる 500 個の ID を持つことができます)。論理:
- Reducer の数を N に設定します (JobConf.setNumReduceTasks(N));
- キーをその範囲に一致させるパーティショナーを実装すると、同じ範囲のすべてのキーが同じリデューサーに送信されます。
- 範囲の最初の id で始まる Reducer 内のカウンターを持ちます (reduce の最初の呼び出しで、パーティショナーと同じロジックを使用して、どの範囲が Reducer であるかを識別します)。
範囲に関するビジネス知識がない場合は、時間をかけてキーを区別し、範囲とその長さを計算することができます。これを使用すると、結果セットで連続した一連の ID を取得できます。
キーを区別することに時間を費やしたくない場合、目標は各リデューサーの ID を異なる数字で開始することです (リデューサー 1 は 1 (1、10、124523、1341243) のみで始まる ID を生成し、リデューサー 2 は 2 で始まります) (2、23、234234532) など)。これを行うには、キーの最初のバイトの mod 10 を計算し、10 個の Reducer を強制し、ゼロを 1 と同じパーティションに向けます (その主な理由は、0 で始まる 2 桁の整数がなく、ID と衝突する可能性があるためです)したがって、パーティション 0 の出力は空です。レデューサー側よりも (2 つの文字列を連結!!!) カウンターを (キー mod 10 の最初のバイト) に追加します。ここで、0 は 1 に変更されます。各レデューサーには 1 から無限大までのカウンターがあり、そのパーティションに使用された最後の ID を含むファイル。
元。
key = "abc", ascii of 'a' is 97, 97 % 10 = 7 and id for that key is '7' + '1' = '71',
for "asd" it will be '7' + '244' = '7244',
for "bbv" is '8' + '1' = '81',
for "bgh" is '8' + '2' = '82',
for "ddv", 'd' is ascii 100, 100 % 100 = 0 , convert to 1, '1' + '1' = '11',
for "edv" is 101 % 100 = 1, '1' + '2234' = '12234'.
すべてのキーは異なる数字で始まるため、重複することはなく、複数の Reducer 間で同期する必要はありません。これは、mod の結果とカウンターの文字列連結によって保証されるため、次のプレフィックス/先頭の数字へのオーバーフローはありません。もう 1 つの利点は、ビジネス ロジックの一部ではない事前並べ替えを行う必要がないことです。Reducer が閉じられると、id 生成で使用された最後のカウンターをファイルに書き込むことができます。これは次の実行で使用され、辞書のパーティションで id の連続性を提供できます。
パーティションの数を 10 から 100 に増やすには、mod 100 を使用し、1 桁の結果を 2 桁の結果とマージします (ここでも、10 個のレデューサーの出力を無駄にします)。Ex 10 % 100 = 10, 1 % 100 = 1 10 に変換, 102 % 100 = 2 文字列として「0」を追加するか、10 を掛けて、20 に変換します。目標は、すべてのプレフィックスの桁数を同じにすることです。 、この場合は 2 です。
巧妙なロジックにより、スキップされたパーティション (mod 100 の場合は 0、または 1、2、9) の無駄を避けることができます。
警告: このロジックは、データの偏りに対して脆弱です。
お役に立てば幸いです、乾杯。
使用する別のオプションは、ID のブロックを task に割り当てることです。これには、トランザクションを管理するメカニズムが必要です。AWS のSimpleDBを使用して、トランザクションと ID の使用の有無を追跡しています。
ジョブの開始時に、Simple DB の ID ブロックのセット全体をロックし (1 つのプロパティのみを「ロック済み」に変更)、各タスクは simple db を呼び出して、使用する ID ブロックを「チェックアウト」します。
タスクが失敗した場合、ブロックが「ロック解除」されず、最後に使用された ID が更新されないため、それらの ID は失敗したタスクによって消費されません。最後に、ID ブロックのセット全体をロック解除すると、失敗したタスクによって保持されていたすべてのブロックがロック解除されます。
外部リソースを更新する必要がないように、単純な DB から ID ブロックをインポート/エクスポートすることで、このプロセスを改善します。代わりに、データとともに保存されたファイルから使用可能な ID を読み取り、単純なデータベースに書き込み、単純な DB を使用して個々のタスク (マッパー、リデューサー) へのブロックの割り当てを調整し、正常な結果でそれらを書き戻すことができます。
その改善を行ったら、おそらくコードを一般公開する予定です。このアプローチに興味がある人は、将来私に連絡してください。完了したら、この投稿をコードで更新します。