在上一个实验中
一个MapReduce 程序示例 细节决定成败(七) :自定义Key 及RecordReader,使用自定义的RecordReader 从split 中读取信息,封装到自定义的key中。
这一步在使用TotalOrderPartitioner 进行词频统计是非常重要的。
下面进入主题:在 一个MapReduce 程序示例 细节决定成败(五) :Partitioner 中,演示了如何使用一下自定义Partitioner。
我们使用了一个自定义的Partitioner,来实现当设置3个reducer时输出字符的排序问题。为了简化问题,我使用在partitioner中直接进行判断。
这里面其实是有两个我们应该注意的问题。
因为TotaOrderlPartitioner是已经封装到hadoop 系统中了,所以我们只要正确配置就可以了。
partition file :描述了每个字符应该进入到哪儿个reducer中的文件,功能就是上一篇中我们简化处理的 a~i 分到0, h~q 分到1,r~z 分到2。
partition file 不用我们手动去写,而且手动去写也很难平均分布各个reducer任务的负载。可以使用InputSampler.RandomSampler完成此项工作的。
关注 getPartitionFile和 new URI 的写法。
结果分析: 下面数据是运行结果,可以看到抽样划分的界限是a~e f~p q~z 。
这一步在使用TotalOrderPartitioner 进行词频统计是非常重要的。
下面进入主题:在 一个MapReduce 程序示例 细节决定成败(五) :Partitioner 中,演示了如何使用一下自定义Partitioner。
我们使用了一个自定义的Partitioner,来实现当设置3个reducer时输出字符的排序问题。为了简化问题,我使用在partitioner中直接进行判断。
这里面其实是有两个我们应该注意的问题。
1、在partitioner代码中写死了,只有3个reducer,那如果我们想要把reducer的个数设置成其它值,还需要改代码。那接下来我们介绍的 TotalOrderPartitioner 就完美解决了上述两个问题。
2、partitioner 的一个作用就是解决数据倾斜的问题,但我们上次写的那个partitioner不适用
本次涉及到的内容有。
1、生成样本partition file
2、使用使用CacheFile功能把partition file 分发到各个map task节点。
3、配置使用TotalOrderPartitioner.
因为TotaOrderlPartitioner是已经封装到hadoop 系统中了,所以我们只要正确配置就可以了。
partition file :描述了每个字符应该进入到哪儿个reducer中的文件,功能就是上一篇中我们简化处理的 a~i 分到0, h~q 分到1,r~z 分到2。
partition file 不用我们手动去写,而且手动去写也很难平均分布各个reducer任务的负载。可以使用InputSampler.RandomSampler完成此项工作的。
下面贴一下配置使用TotaOrderlPartitioner的代码
默认抽样是在所有的split上进行的,当输入文件很大的时候,使用第一个方法进行抽样代价会很大。点击(此处)折叠或打开
- /**
- * freq:每个key 可以被抽样次数
- * numSamples:总样本数
- */
- InputSampler.RandomSampler(double freq, int numSamples);
- /**
- * freq:每个key 可以被抽样次数
- * numSamples:总样本数
- * maxSplitsSampled:定义最多从多少个splits 上进行抽样
- */
- InputSampler.RandomSampler(double freq, int numSamples, int maxSplitsSampled);
当处理文件很大的时候,加上第三个参数maxSplitsSampled 限定最多处理多少个split。
freq 参数: 被抽样到的split中的每一个key 被选中的概率。 numSamples 代表抽样的总样本数。 比如从10000条数据中抽取 10条数,假如freq 设置为0.5,那按概率大约执行到20条的时候,样本数就够了。
但然后程序会继续执行,每个被抽中的样本去随机替换一条已经抽出的10条记录中的一条,一直到最后。
关注 getPartitionFile和 new URI 的写法。
点击(此处)折叠或打开
- job.setPartitionerClass(TotalOrderPartitioner.class);
- job.setNumReduceTasks(3);
- RandomSampler<MyKey, NullWritable> sampler
- = new InputSampler.RandomSampler<MyKey,NullWritable>(0.6, 20, 3);
- InputSampler.writePartitionFile(job, sampler);
-
- String partitionFile = TotalOrderPartitioner.getPartitionFile(job.getConfiguration());
- URI uri = new URI(partitionFile+"#"+TotalOrderPartitioner.DEFAULT_PATH);
- job.addCacheFile(uri);
注意: 1、RecordReader的key与map输入输出key和reduce的输入key 要保持一致。 因为取样是从job 配置的InputFormat中得到RecordReader 然后不断的得到Key ,从中取样。
取到的样使用TotalOrderPartitioner 得到的partitionFile 是供分配到不同reducer的,所以RecordReader的key 要与Reduer的输入key 一致,又 RecordReader的key value 是输入到 map 中的,
Reduce 的输入又是Map的输出。绕了半天结论就是: RecordReader的key, Map 的输入输出key ,Reduce的输入key 是要保持一致的。
2、使用TotalOrderPartitioner前一定要配置了reduceTask 的个数、InputFormat、和上面讨论的 key class。
贴一下结果:
点击(此处)折叠或打开
- wordcount.MyKey@212cb585 8
- wordcount.MyKey@212cb585 3
- wordcount.MyKey@212cb585 4
- wordcount.MyKey@212cb585 4
- wordcount.MyKey@212cb585 11
- wordcount.MyKey@212cb585 4
- wordcount.MyKey@212cb585 3
- wordcount.MyKey@168497f6 8
- wordcount.MyKey@168497f6 5
- wordcount.MyKey@168497f6 3
- wordcount.MyKey@168497f6 3
- wordcount.MyKey@168497f6 6
- wordcount.MyKey@168497f6 7
- wordcount.MyKey@168497f6 4
- wordcount.MyKey@168497f6 12
- wordcount.MyKey@168497f6 3
- wordcount.MyKey@168497f6 3
- wordcount.MyKey@2e67cd84 13
- wordcount.MyKey@2e67cd84 4
- wordcount.MyKey@2e67cd84 4
- wordcount.MyKey@2e67cd84 6
- wordcount.MyKey@2e67cd84 7
- wordcount.MyKey@2e67cd84 3
- wordcount.MyKey@2e67cd84 6
- wordcount.MyKey@2e67cd84 3
注意到key的格式,这是由于我们的MyKey没有覆写toString 方法,输出的时候直接使用了Object的toString实现。
点击(此处)折叠或打开
- @Override
- public String toString(){
- return String.valueOf(this.c);
- }
点击(此处)折叠或打开
- [train@sandbox src]$ hdfs dfs -cat output/*0
- a 8
- b 3
- c 4
- d 4
- e 11
- [train@sandbox src]$ hdfs dfs -cat output/*1
- f 4
- g 3
- h 8
- i 5
- j 3
- k 3
- l 6
- m 7
- n 4
- o 12
- p 3
- [train@sandbox src]$ hdfs dfs -cat output/*2
- q 3
- r 13
- s 4
- t 4
- u 6
- w 7
- x 3
- y 6
- z 3
-
下面贴出主程序代码
点击(此处)折叠或打开
- package wordcount;
-
- import java.io.IOException;
- import java.net.URI;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.conf.Configured;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.NullWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapred.lib.TotalOrderPartitioner;
- 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.TextInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
- import org.apache.hadoop.mapreduce.lib.partition.InputSampler;
- import org.apache.hadoop.mapreduce.lib.partition.InputSampler.RandomSampler;
- import org.apache.hadoop.util.Tool;
- import org.apache.hadoop.util.ToolRunner;
- import org.apache.log4j.Logger;
-
- public class MyWordCountJob extends Configured implements Tool {
- Logger log = Logger.getLogger(MyWordCountJob.class);
-
- public static class MyWordCountMapper extends
- Mapper<MyKey, NullWritable, MyKey, IntWritable> {
- Text mKey = new Text();
- IntWritable mValue = new IntWritable(1);
- @Override
- protected void map(MyKey key, NullWritable value, Context context)
- throws IOException, InterruptedException {
- context.write(key, mValue);
- }
- }
-
- public static class MyWordCountReducer extends Reducer<MyKey, IntWritable, MyKey, IntWritable> {
- Text rkey = new Text();
- IntWritable rvalue = new IntWritable(1);
- @Override
- protected void reduce(MyKey key, Iterable<IntWritable> values,Context context)
- throws IOException, InterruptedException {
-
- int n=0;
- for(IntWritable value :values){
- n+= value.get();
- }
- rvalue.set(n);
- context.write(key, rvalue);
- }
- }
-
- @Override
- public int run(String[] args) throws Exception {
- //valid the parameters
- if(args.length !=2){
- return -1;
- }
-
- Job job = Job.getInstance(getConf(), "MyWordCountJob");
- job.setJarByClass(MyWordCountJob.class);
-
- Path inPath = new Path(args[0]);
- Path outPath = new Path(args[1]);
-
- outPath.getFileSystem(getConf()).delete(outPath,true);
- TextInputFormat.setInputPaths(job, inPath);
- TextOutputFormat.setOutputPath(job, outPath);
-
-
- job.setMapperClass(MyWordCountJob.MyWordCountMapper.class);
- job.setReducerClass(MyWordCountJob.MyWordCountReducer.class);
-
- job.setInputFormatClass(MyCombinedFilesInputFormat.class);
- MyCombinedFilesInputFormat.setMaxInputSplitSize(job, 1024*1024*64);
- job.setOutputFormatClass(TextOutputFormat.class);
- job.setMapOutputKeyClass(MyKey.class);
- job.setMapOutputValueClass(IntWritable.class);
- job.setOutputKeyClass(MyKey.class);
- job.setOutputValueClass(IntWritable.class);
-
- job.setPartitionerClass(TotalOrderPartitioner.class);
- job.setNumReduceTasks(3);
- RandomSampler<MyKey, NullWritable> sampler
- = new InputSampler.RandomSampler<MyKey,NullWritable>(0.6, 20, 3);
- InputSampler.writePartitionFile(job, sampler);
-
- String partitionFile = TotalOrderPartitioner.getPartitionFile(job.getConfiguration());
- URI uri = new URI(partitionFile+"#"+TotalOrderPartitioner.DEFAULT_PATH);
- job.addCacheFile(uri);
-
- return job.waitForCompletion(true)?0:1;
- }
- public static void main(String [] args){
- int result = 0;
- try {
- result = ToolRunner.run(new Configuration(), new MyWordCountJob(), args);
- } catch (Exception e) {
- e.printStackTrace();
- }
- System.exit(result);
- }
-
- }
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30066956/viewspace-2109331/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/30066956/viewspace-2109331/