多くの MapReduce プログラムでは、リデューサーがコンバイナーとしても使用されています。これは、これらのプログラムの特定の性質によるものであることはわかっています。しかし、私はそれらが異なることができるかどうか疑問に思っています。
4 に答える
はい、コンバイナーは Reducer とは異なる場合がありますが、Combiner は引き続き Reducer インターフェイスを実装します。コンバイナーは、ジョブに依存する特定のケースでのみ使用できます。コンバイナーはリデューサーのように動作しますが、各マッパーから出力されたキー/値のサブセットに対してのみです。
Reducer とは異なり、Combiner が持つ 1 つの制約は、入力/出力のキーと値の型が Mapper の出力型と一致する必要があることです。
ええ、それらは確かに異なる可能性がありますが、ほとんどの場合、予期しない結果が得られるため、別のクラスを使用したいとは思いません。
コンバイナは、commutative(ab = ba) および連想 {a.(bc) = (ab).c} である関数でのみ使用できます。これは、コンバイナーがキーと値のサブセットでのみ動作するか、まったく実行されない可能性があることも意味しますが、それでもプログラムの出力は同じままにしたい.
異なるロジックを持つ別のクラスを選択すると、論理的な出力が得られない場合があります。
これが実装です。コンバイナーなしで実行することも、コンバイナーを使用することもできます。どちらもまったく同じ答えを返します。ここで、Reducer と Combiner は異なる動機と異なる実装を持っています。
package combiner;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class Map extends Mapper<LongWritable, Text, Text, Average> {
Text name = new Text();
String[] row;
protected void map(LongWritable offSet, Text line, Context context) throws IOException, InterruptedException {
row = line.toString().split(" ");
System.out.println("Key "+row[0]+"Value "+row[1]);
name.set(row[0]);
context.write(name, new Average(Integer.parseInt(row[1].toString()), 1));
}}
クラスを減らす
public class Reduce extends Reducer<Text, Average, Text, LongWritable> {
LongWritable avg =new LongWritable();
protected void reduce(Text key, Iterable<Average> val, Context context)throws IOException, InterruptedException {
int total=0; int count=0; long avgg=0;
for (Average value : val){
total+=value.number*value.count;
count+=value.count;
avgg=total/count;
}
avg.set(avgg);
context.write(key, avg);
}
}
MapObject クラス
public class Average implements Writable {
long number;
int count;
public Average() {super();}
public Average(long number, int count) {
this.number = number;
this.count = count;
}
public long getNumber() {return number;}
public void setNumber(long number) {this.number = number;}
public int getCount() {return count;}
public void setCount(int count) {this.count = count;}
@Override
public void readFields(DataInput dataInput) throws IOException {
number = WritableUtils.readVLong(dataInput);
count = WritableUtils.readVInt(dataInput);
}
@Override
public void write(DataOutput dataOutput) throws IOException {
WritableUtils.writeVLong(dataOutput, number);
WritableUtils.writeVInt(dataOutput, count);
}
}
コンバイナークラス
public class Combine extends Reducer<Text, Average, Text, Average>{
protected void reduce(Text name, Iterable<Average> val, Context context)throws IOException, InterruptedException {
int total=0; int count=0; long avg=0;
for (Average value : val){
total+=value.number;
count+=1;
avg=total/count;
}
context.write(name, new Average(avg, count));
}
}
ドライバークラス
public class Driver1 {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
if (args.length != 2) {
System.err.println("Usage: SecondarySort <in> <out>");
System.exit(2);
}
Job job = new Job(conf, "CustomCobiner");
job.setJarByClass(Driver1.class);
job.setMapperClass(Map.class);
job.setCombinerClass(Combine.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Average.class);
job.setReducerClass(Reduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
ここからコードを Git
あなたの提案を残して..
コンバイナーの主な目的は、マッパーとリデューサーの間でネットワーク全体でシャッフルされるキーと値のペアの数を最適化/最小化し、可能な限り多くの帯域幅を節約することです。
コンバイナーの経験則は、入力変数と出力変数の型が同じでなければならないということです。その理由は、コンバイナーの使用は保証されておらず、スピルの量と数に応じて、使用できるかできないかです。
レデューサーは、この規則を満たす場合、つまり入力変数と出力変数の型が同じ場合、コンバイナーとして使用できます。
コンバイナのもう 1 つの最も重要なルールは、適用する関数が可換かつ結合的である場合にのみ使用できるということです。数字を追加するようなものですが、平均のような場合はそうではありません(レデューサーと同じコードを使用している場合)。
あなたの質問に答えるために、はい、もちろんそれらは異なる可能性があります。レデューサーに異なるタイプの入力変数と出力変数がある場合、選択の余地はありませんが、レデューサーコードの別のコピーを作成して変更する必要があります。
レデューサーのロジックについて心配している場合は、別の方法で実装することもできます。たとえば、コンバイナーの場合、コンバイナーに到達するすべての値のローカル バッファーを持つコレクション オブジェクトを使用できます。レデューサーで使用するよりもリスクがあります。これは、レデューサーの場合、コンバイナーよりもメモリ不足になりやすいためです。他の論理的な違いは確かに存在する可能性があり、実際に存在します。