我对Hadoop Map / Reduce相当新。我正在尝试编写一个Map / Reduce作业来查找n个进程所花费的平均时间,给定一个输入文本文件如下:
ProcessName Time
process1 10
process2 20
processn 30
我经历了一些教程,但我仍然无法彻底了解。我的mapper和reducer类应该为这个问题做些什么?我的输出总是一个文本文件,还是可以直接将平均值存储在某种变量中?
感谢。
答案 0 :(得分:3)
您的映射器将您的输入映射到您想要取平均值的值。因此,假设您的输入是一个格式为
的文本文件ProcessName Time
process1 10
process2 20
.
.
.
然后,您需要获取文件中的每一行,拆分它,抓取第二列,并将该列的值输出为IntWritable
(或其他Writable
数字类型)。由于您希望获取所有时间的平均值,而不是按进程名称或任何内容进行分组,因此您将拥有一个固定密钥。因此,您的映射器看起来像
private IntWritable one = new IntWritable(1);
private IntWritable output = new IntWritable();
proctected void map(LongWritable key, Text value, Context context) {
String[] fields = value.split("\t");
output.set(Integer.parseInt(fields[1]));
context.write(one, output);
}
您的reducer采用这些值,并简单地计算平均值。这看起来像
IntWritable one = new IntWritable(1);
DoubleWritable average = new DoubleWritable();
protected void reduce(IntWritable key, Iterable<IntWrtiable> values, Context context) {
int sum = 0;
int count = 0;
for(IntWritable value : values) {
sum += value.get();
count++;
}
average.set(sum / (double) count);
context.Write(key, average);
}
我在这里做了很多假设,关于你的输入格式和什么不是,但它们是合理的假设,你应该能够根据你的确切需要进行调整。
我的输出总是一个文本文件,还是可以直接将平均值存储在某种变量中?
你有几个选择。您可以对作业的输出进行后处理(写入单个文件),或者,因为您正在计算单个值,所以您可以将结果存储在计数器中,例如。
答案 1 :(得分:2)
您的Mappers读取文本文件并在每一行应用以下地图功能
map: (key, value)
time = value[2]
emit("1", time)
所有地图调用都会发出键“1”,该键将由一个reduce函数
处理reduce: (key, values)
result = sum(values) / n
emit("1", result)
由于您正在使用Hadoop,您可能已经在map函数中看到过使用StringTokenizer,您可以使用它来获取一行中的时间。您还可以考虑一些如何计算n(进程数)的方法,例如,您可以在另一个只计算行数的作业中使用计数器。
更新
如果要执行此作业,则每个行都必须将一个元组发送到reducer,如果在多台计算机上运行Hadoop集群,可能会堵塞网络。
更聪明的方法可以计算更接近输入的时间总和,例如通过指定组合器:
combine: (key, values)
emit(key, sum(values))
然后,对同一机器的所有映射函数的结果执行该组合器,即,在它们之间没有联网。
然后,reducer将只获得与群集中的计算机一样多的元组,而不是日志文件中的行数。