使用combiner提升性能

原创 2012年03月29日 15:11:51

在上一节的AverageByAttributeMapper.py和AverageByAttributeReducer.py中计算平均值,由mapper读取<属性,1>,在网络上进行洗牌,reducer计算每个键的平均值。可以看到至少有两个效率瓶颈:

  1. 如果有10亿条记录,mapper会生成10亿个 键值对 在网络上洗牌,实际上,在求最大值时,mapper只需要输出键中最大的一个。求平均值则可以重新定义算法,使每个键只有一个记录参与洗牌。
  2. 若使用国家作为键,会出现 数据倾斜,不是均匀分布,导致一个reducer中进入很多数据。

 可以使用combiner先进行本地reduce,它能有效的减少mapper的输出以降低网络和reducer上的压力。另外注意,combine在数据转换上需与reducer等价。就是说,如果去掉combiner,输出应该保持不变。

对于分配型函数,如最大值,最小值,求和等,可以使用reducer直接作为combiner。对于其他如平均值,进行部分转换即可。

如下AverageByAttribute.java求平均值:

不加combiner代码:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;


public class AverageByAttribute extends Configured implements Tool {
	public static class MapClass extends Mapper<LongWritable, Text, Text, Text>
	{
		public void map(LongWritable key, Text value, Context context)throws IOException, InterruptedException
		{
			String [] lines = value.toString().split(",");
			String country = lines[4];
			String numClaims = lines[8];
			//length > 0 可以排除未声明专利得国家,startswith则排除第一行属性名
			if(numClaims.length() > 0 && !numClaims.startsWith("\""))
			{
				//这里作为v2的Text值分别存储了声明的专利数,和计数count,用于在reduce过程中num/count计算平均值
				context.write(new Text(country), new Text(numClaims + ",1"));
			}
		}
	}
	
	public static class Reduce extends Reducer<Text, Text, Text, DoubleWritable>
	{
		public void reduce(Text key, Iterable<Text> values, Context context)throws IOException, InterruptedException
		{
			double sum = 0.0;
			int count = 0;
			for(Text element: values)
			{
				String [] fields = element.toString().split(",");
				sum += Double.parseDouble(fields[0]);
				count += Integer.parseInt(fields[1]);
			}
			context.write(key, new DoubleWritable(sum/count));
		}
	}

	@Override
	public int run(String[] args) throws Exception {
		
		Configuration conf = new Configuration();
		Job job = new Job(conf,"AverageByAttribute");
		//注意:此处设置得输出key\value是map阶段结束后的key\value对,开始把它当成了最终输出,导致一直提示类型不匹配!!
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);
		
		job.setMapperClass(MapClass.class);
		job.setReducerClass(Reduce.class);
		
		job.setInputFormatClass(TextInputFormat.class);
		job.setOutputFormatClass(TextOutputFormat.class);
		
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		//成功结束时返回0,失败时返回1
		System.exit(job.waitForCompletion(true)? 0: 1);
		return 0;
	}
	
	public static void main(String [] args) throws Exception
	{
		int res = ToolRunner.run(new Configuration(), new AverageByAttribute(), args);
		System.exit(res);
	}
}
添加combiner后,代码如下:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

//这个程序与AverageByAttribute作用完全相同,但我们采用了combiner来实现国家专利数的累加和计数,只在reducer中进行求平均值操作
//相当于将reducer的一部分工作放到了combiner中,所以combiner要实现reducer接口,这在分配型函数中非常常见!!
public class AverageCombiner extends Configured implements Tool {
	public static class MapClass extends Mapper<LongWritable, Text, Text, Text>
	{
		public void map(LongWritable key, Text value, Context context)throws IOException, InterruptedException
		{
			String [] lines = value.toString().split(",");
			String country = lines[4];
			String numClaims = lines[8];
			//length > 0 可以排除未声明专利得国家,startswith则排除第一行属性名
			if(numClaims.length() > 0 && !numClaims.startsWith("\""))
			{
				//这里作为v2的Text值分别存储了声明的专利数,和计数count,用于在reduce过程中num/count计算平均值
				context.write(new Text(country), new Text(numClaims + ",1"));
			}
		}
	}
	
	public static class Combine extends Reducer<Text, Text, Text, Text>
	{
		public void reduce(Text key, Iterable<Text> values, Context context)throws IOException, InterruptedException
		{
			double sum = 0.0;
			int count = 0;
			for(Text element: values)
			{
				String [] fields = element.toString().split(",");
				sum += Double.parseDouble(fields[0]);
				count += Integer.parseInt(fields[1]);
			}
			//由于","的存在,在sum+","+count中会自动调用两者得toString方法,最终得到一个字符串。
			context.write(key, new Text(sum + "," + count));
		}
	}
	public static class Reduce extends Reducer<Text, Text, Text, DoubleWritable>
	{
		//注意,尽管添加了combiner,此处传入的values仍然需要是Iterable<>泛型,因为combiner只是对每一个map本地进行reduce
		//输入仍是分片的,但是减少了很多需要洗牌的记录,开始没有加Iterable<>,发现结果只计算了一半。。。
		public void reduce(Text key, Iterable<Text> values, Context context)throws IOException, InterruptedException
		{
			double sum = 0.0;
			int count = 0;
			for(Text element: values)
			{
				String [] fields = element.toString().split(",");
				sum += Double.parseDouble(fields[0]);
				count += Integer.parseInt(fields[1]);
			}
			//由于","的存在,在sum+","+count中会自动调用两者得toString方法,最终得到一个字符串。
			context.write(key, new DoubleWritable(sum/count));
		}
	}

	@Override
	public int run(String[] args) throws Exception {
		
		Configuration conf = new Configuration();
		Job job = new Job(conf,"AverageCombiner");
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);
		
		job.setMapperClass(MapClass.class);
		job.setCombinerClass(Combine.class);
		job.setReducerClass(Reduce.class);
		
		job.setInputFormatClass(TextInputFormat.class);
		job.setOutputFormatClass(TextOutputFormat.class);
		
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		//成功结束时返回0,失败时返回1
		System.exit(job.waitForCompletion(true)? 0: 1);
		return 0;
	}
	
	public static void main(String [] args) throws Exception
	{
		int res = ToolRunner.run(new Configuration(), new AverageCombiner(), args);
		System.exit(res);
	}
}
比较可知,combiner几乎与reducer相同,reducer多计算了一个sum/count表示平均值

相关文章推荐

Wordcount Combiner性能测试

在mapreduce程序的运行过程中,map阶段产生的数据存放在本地磁盘中,reduce阶段节点通过远程访问的形式读取进行下一步计算。如果能够减少map阶段产生的数据量就能够大幅度提升体统性能。 ...

combiner的使用误区

问题提出: 众所周知,Hadoop框架使用Mapper将数据处理成一个键值对,再网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。    在上述过程中,我们看到...

mapreduce编程练习(二)倒排索引 Combiner的使用以及练习

hadoop mapreduce编程 练习 倒排索引 Combiner的使用以及练习

Hadoop中Combiner的使用

在MapReduce中,当map生成的数据过大时,带宽就成了瓶颈,怎样精简压缩传给Reduce的数据,有不影响最终的结果呢。有一种方法就是使用Combiner,Combiner号称本地的Reduce,...
  • iPolaris
  • iPolaris
  • 2013年03月26日 21:15
  • 19348

Hadoop 使用Combiner提高Map/Reduce程序效率

众所周知,Hadoop框架使用Mapper将数据处理成一个键值对,再网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。     在上述过程中,我们看到至少两个性能...

Mapreduce中Combiner的使用及误区

问题提出: 众所周知,Hadoop框架使用Mapper将数据处理成一个键值对,再网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。    在上述过程中,我们看到...
  • guoery
  • guoery
  • 2013年01月22日 11:18
  • 17553

Mapreduce中Combiner的使用及误区

转载:http://blog.csdn.net/guoery/article/details/8529004 问题提出:     众所周知,Hadoop框架使用Mapper将数据处理成...

Mapreduce中Combiner的使用及误区

问题提出: 众所周知,Hadoop框架使用Mapper将数据处理成一个键值对,再网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。    在上述过程中,我们看到...

pig优化器之一(翻译pig wiki):combiner使用

Combiner

pdf combiner

  • 2013年10月30日 13:28
  • 1.47MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用combiner提升性能
举报原因:
原因补充:

(最多只允许输入30个字)