これは非常に良い質問です。Hadoop の単語カウントの例の非効率性に気付いたからです。
問題を最適化するための秘訣は次のとおりです。
HashMap
ローカル マップ ステージでベースのグループ化を行います。そのためにコンバイナーを使用することもできます。これは次のようになります。私はHashMultiSet
Guava を使用しています。これにより、優れたカウント メカニズムが促進されます。
public static class WordFrequencyMapper extends
Mapper<LongWritable, Text, Text, LongWritable> {
private final HashMultiset<String> wordCountSet = HashMultiset.create();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] tokens = value.toString().split("\\s+");
for (String token : tokens) {
wordCountSet.add(token);
}
}
そして、クリーンアップ ステージで結果を出力します。
@Override
protected void cleanup(Context context) throws IOException,
InterruptedException {
Text key = new Text();
LongWritable value = new LongWritable();
for (Entry<String> entry : wordCountSet.entrySet()) {
key.set(entry.getElement());
value.set(entry.getCount());
context.write(key, value);
}
}
つまり、作業のローカル ブロックに単語をグループ化して、RAM を少し使用することでネットワークの使用量を削減しました。で同じことを行うこともできますが、Combiner
グループに並べ替えているため、HashMultiset
.
トップ N を取得するには、そのローカルのトップ N をHashMultiset
出力コレクターに書き込み、reduce 側で通常の方法で結果を集計するだけです。これにより、ネットワーク帯域幅も大幅に節約できます。唯一の欠点は、クリーンアップ メソッドでワード カウント タプルをソートする必要があることです。
コードの一部は次のようになります。
Set<String> elementSet = wordCountSet.elementSet();
String[] array = elementSet.toArray(new String[elementSet.size()]);
Arrays.sort(array, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// sort descending
return Long.compare(wordCountSet.count(o2), wordCountSet.count(o1));
}
});
Text key = new Text();
LongWritable value = new LongWritable();
// just emit the first n records
for(int i = 0; i < N, i++){
key.set(array[i]);
value.set(wordCountSet.count(array[i]));
context.write(key, value);
}
できるだけ多くの単語をローカルで実行する要点を理解してから、上位 N の上位 N を集計してください ;)