Hadoop阅读笔记(二)——利用MapReduce求平均数和去重
前言:圣诞节来了,我怎么能虚度光阴呢?!依稀记得,那一年,大家互赠贺卡,短短几行字,字字融化在心里;那一年,大家在水果市场,寻找那些最能代表自己心意的苹果香蕉梨,摸着冰冷的水果外皮,内心早已滚烫。这一年……我在博客园-_-#,希望用dt的代码燃烧脑细胞,温暖小心窝。
上篇《Hadoop阅读笔记(一)——强大的MapReduce》主要介绍了MapReduce的在大数据集上处理的优势以及运行机制,通过专利数据编写Demo加深了对于MapReduce中输入输出数据结构的细节理解。有了理论上的指导,仍需手捧我的hadoop圣经——Hadoop实战2,继续走完未走过的路(有种怪怪的感觉,我一直在原地踏步)。
正文:实践是检验真理的唯一标准,不知道这是谁说的,但是作为码农,倒也是实用受用的座右铭。除却大牛们能够在阅读高深理论时new一个并发线程,眼睛所到之处,已然可以理清program的精髓所在,好似一台计算机扫描了所看到的代码一般(有点夸张^_^)。作为普罗大众的搬砖者来说,还是通过一些实例来加深对于理论的认识。
今天主要是通过以下两个例子:求平均成绩、去重来加深对MapReduce的理解。
1.如何用MapReduce求平均成绩——WordCount的加强版
在谈平均成绩之前我们回顾下属性的Hadoop HelloWorld程序——WordCount,其主要是统计数据集中各个单词出现的次数。因为次数没有多少之分,如果将这里的次数换成分数就将字数统计问题转化成了求每个个体的总成绩的问题,再加上一步(总成绩/学科数)运算就是这里要讨论的求平均数的问题了。在笔者看来,MapReduce是一种编程思维,它引导码农们如何将一个亟待解决的问题转换为一个MapReduce过程:map阶段输入什么、map过程执行什么、map阶段输出什么、reduce阶段输入什么、执行什么、输出什么。能够将以上几个点弄清楚整明白,一个MapReduce程序就会跃然纸上。这里:
Map: 指定格式的数据集(如"张三 60")——输入数据
执行每条记录的分割操作以key-value写入上下文context中——执行功能
得到指定键值对类型的输出(如"(new Text(张三),new IntWritable(60))")——输出结果
Reduce: map的输出——输入数据
求出单个个体的总成绩后再除以该个体课程数目——执行功能
得到指定键值对类型的输入——输出结果
鉴于上面的map和reduce过程,我们可以得到如下的代码:
- public class Test1214 {
- public static class MapperClass extends Mapper<LongWritable, Text, Text, IntWritable> {
- public void map(LongWritable key, Text value, Context context){
- String line = value.toString();
- System.out.println("该行数据为:" + line);
- StringTokenizer token = new StringTokenizer(line,"\t");
- String nameT = token.nextToken();
- int score = Integer.parseInt(token.nextToken());
- Text name = new Text(nameT);
- try {
- context.write(name, new IntWritable(score));
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public static class ReducerClass extends Reducer<Text, IntWritable, Text, IntWritable>{
- public void reduce(Text key, Iterable<IntWritable> value, Context context){
- int sum = 0;
- int count =0;
- for(IntWritable score : value){
- sum += score.get();
- count++;
- System.out.println("第" + count + "个数值为:" + score.get());
- }
- IntWritable avg = new IntWritable(sum/count);
- try {
- context.write(key, avg);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * @param args
- * @throws IOException
- * @throws ClassNotFoundException
- * @throws InterruptedException
- */
- public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
- Configuration conf = new Configuration();
- String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
- if (otherArgs.length != 2) {
- System.err.println("Usage: wordcount <in> <out>");
- System.exit(2);
- }
- Job job = new Job(conf, "Test1214");
- job.setJarByClass(Test1214.class);
- job.setMapperClass(MapperClass.class);
- job.setCombinerClass(ReducerClass.class);
- job.setReducerClass(ReducerClass.class);
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(IntWritable.class);
- org.apache.hadoop.mapreduce.lib.input.FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
- org.apache.hadoop.mapreduce.lib.output.FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
- System.exit(job.waitForCompletion(true) ? 0 : 1);
- System.out.println("end");
- }
- }
数据集:这里的数据是码农我自己手工创建的,主要是想看看mapreduce的运行过程,所以就创建了两个文件,当然这里面的成绩也就没有什么是否符合正态分布的考虑了……
数据中设定有A-K共11个学生,共16门课程,具体数据如下:
NameScore1.txt:
- A 55
- B 65
- C 44
- D 87
- E 66
- F 90
- G 70
- H 59
- I 61
- J 58
- K 40
- A 45
- B 62
- C 64
- D 77
- E 36
- F 50
- G 80
- H 69
- I 71
- J 70
- K 49
- A 51
- B 64
- C 74
- D 37
- E 76
- F 80
- G 50
- H 51
- I 81
- J 68
- K 80
- A 85
- B 55
- C 49
- D 67
- E 69
- F 50
- G 80
- H 79
- I 81
- J 68
- K 80
- A 35
- B 55
- C 40
- D 47
- E 60
- F 72
- G 76
- H 79
- I 68
- J 78
- K 50
- A 65
- B 45
- C 74
- D 57
- E 56
- F 50
- G 60
- H 59
- I 61
- J 58
- K 60
- A 85
- B 45
- C 74
- D 67
- E 86
- F 70
- G 50
- H 79
- I 81
- J 78
- K 60
- A 50
- B 69
- C 40
- D 89
- E 69
- F 95
- G 75
- H 59
- I 60
- J 59
- K 45
NameScore2.txt:
- A 65
- B 75
- C 64
- D 67
- E 86
- F 70
- G 90
- H 79
- I 81
- J 78
- K 60
- A 65
- B 82
- C 84
- D 97
- E 66
- F 70
- G 80
- H 89
- I 91
- J 90
- K 69
- A 71
- B 84
- C 94
- D 67
- E 96
- F 80
- G 70
- H 71
- I 81
- J 98
- K 80
- A 85
- B 75
- C 69
- D 87
- E 89
- F 80
- G 70
- H 99
- I 81
- J 88
- K 60
- A 65
- B 75
- C 60
- D 67
- E 80
- F 92
- G 76
- H 79
- I 68
- J 78
- K 70
- A 85
- B 85
- C 74
- D 87
- E 76
- F 60
- G 60
- H 79
- I 81
- J 78
- K 80
- A 85
- B 65
- C 74
- D 67
- E 86
- F 70
- G 70
- H 79
- I 81
- J 78
- K 60
- A 70
- B 69
- C 60
- D 89
- E 69
- F 95
- G 75
- H 59
- I 60
- J 79
- K 65
其执行过程中控制台打印的信息为:
- 14/12/14 17:05:27 INFO jvm.JvmMetrics: Initializing JVM Metrics with processName=JobTracker, sessionId=
- 14/12/14 17:05:27 WARN mapred.JobClient: No job jar file set. User classes may not be found. See JobConf(Class) or JobConf#setJar(String).
- 14/12/14 17:05:27 INFO input.FileInputFormat: Total input paths to process : 2
- 14/12/14 17:05:27 INFO mapred.JobClient: Running job: job_local_0001
- 14/12/14 17:05:27 INFO input.FileInputFormat: Total input paths to process : 2
- 14/12/14 17:05:27 INFO mapred.MapTask: io.sort.mb = 100
- 14/12/14 17:05:28 INFO mapred.MapTask: data buffer = 79691776/99614720
- 14/12/14 17:05:28 INFO mapred.MapTask: record buffer = 262144/327680
- 该行数据为:A 55
- 该行数据为:B 65
- 该行数据为:C 44
- 该行数据为:D 87
- 该行数据为:E 66
- 该行数据为:F 90
- 该行数据为:G 70
- 该行数据为:H 59
- 该行数据为:I 61
- 该行数据为:J 58
- 该行数据为:K 40
- 该行数据为:A 45
- 该行数据为:B 62
- 该行数据为:C 64
- 该行数据为:D 77
- 该行数据为:E 36
- 该行数据为:F 50
- 该行数据为:G 80
- 该行数据为:H 69
- 该行数据为:I 71
- 该行数据为:J 70
- 该行数据为:K 49
- 该行数据为:A 51
- 该行数据为:B 64
- 该行数据为:C 74
- 该行数据为:D 37
- 该行数据为:E 76
- 该行数据为:F 80
- 该行数据为:G 50
- 该行数据为:H 51
- 该行数据为:I 81
- 该行数据为:J 68
- 该行数据为:K 80
- 该行数据为:A 85
- 该行数据为:B 55
- 该行数据为:C 49
- 该行数据为:D 67
- 该行数据为:E 69
- 该行数据为:F 50
- 该行数据为:G 80
- 该行数据为:H 79
- 该行数据为:I 81
- 该行数据为:J 68
- 该行数据为:K 80
- 该行数据为:A 35
- 该行数据为:B 55
- 该行数据为:C 40
- 该行数据为:D 47
- 该行数据为:E 60
- 该行数据为:F 72
- 该行数据为:G 76
- 该行数据为:H 79
- 该行数据为:I 68
- 该行数据为:J 78
- 该行数据为:K 50
- 该行数据为:A 65
- 该行数据为:B 45
- 该行数据为:C 74
- 该行数据为:D 57
- 该行数据为:E 56
- 该行数据为:F 50
- 该行数据为:G 60
- 该行数据为:H 59
- 该行数据为:I 61
- 该行数据为:J 58
- 该行数据为:K 60
- 该行数据为:A 85
- 该行数据为:B 45
- 该行数据为:C 74
- 该行数据为:D 67
- 该行数据为:E 86
- 该行数据为:F 70
- 该行数据为:G 50
- 该行数据为:H 79
- 该行数据为:I 81
- 该行数据为:J 78
- 该行数据为:K 60
- 该行数据为:A 50
- 该行数据为:B 69
- 该行数据为:C 40
- 该行数据为:D 89
- 该行数据为:E 69
- 该行数据为:F 95
- 该行数据为:G 75
- 该行数据为:H 59
- 该行数据为:I 60
- 该行数据为:J 59
- 该行数据为:K 45
- 14/12/14 17:05:28 INFO mapred.MapTask: Starting flush of map output
- 第1个数值为:55
- 第2个数值为:45
- 第3个数值为:51
- 第4个数值为:85
- 第5个数值为:35
- 第6个数值为:65
- 第7个数值为:85
- 第8个数值为:50
- 第1个数值为:45
- 第2个数值为:64
- 第3个数值为:65
- 第4个数值为:45
- 第5个数值为:55
- 第6个数值为:69
- 第7个数值为:62
- 第8个数值为:55
- 第1个数值为:64
- 第2个数值为:49
- 第3个数值为:44
- 第4个数值为:74
- 第5个数值为:74
- 第6个数值为:40
- 第7个数值为:40
- 第8个数值为:74
- 第1个数值为:67
- 第2个数值为:67
- 第3个数值为:77
- 第4个数值为:37
- 第5个数值为:87
- 第6个数值为:57
- 第7个数值为:89
- 第8个数值为:47
- 第1个数值为:36
- 第2个数值为:66
- 第3个数值为:76
- 第4个数值为:86
- 第5个数值为:69
- 第6个数值为:69
- 第7个数值为:60
- 第8个数值为:56
- 第1个数值为:90
- 第2个数值为:95
- 第3个数值为:70
- 第4个数值为:50
- 第5个数值为:80
- 第6个数值为:50
- 第7个数值为:50
- 第8个数值为:72
- 第1个数值为:60
- 第2个数值为:76
- 第3个数值为:50
- 第4个数值为:50
- 第5个数值为:80
- 第6个数值为:70
- 第7个数值为:75
- 第8个数值为:80
- 第1个数值为:59
- 第2个数值为:69
- 第3个数值为:51
- 第4个数值为:79
- 第5个数值为:59
- 第6个数值为:79
- 第7个数值为:59
- 第8个数值为:79
- 第1个数值为:60
- 第2个数值为:61
- 第3个数值为:81
- 第4个数值为:81
- 第5个数值为:61
- 第6个数值为:71
- 第7个数值为:68
- 第8个数值为:81
- 第1个数值为:58
- 第2个数值为:59
- 第3个数值为:78
- 第4个数值为:68
- 第5个数值为:78
- 第6个数值为:68
- 第7个数值为:70
- 第8个数值为:58
- 第1个数值为:40
- 第2个数值为:50
- 第3个数值为:49
- 第4个数值为:60
- 第5个数值为:60
- 第6个数值为:45
- 第7个数值为:80
- 第8个数值为:80
- 14/12/14 17:05:28 INFO mapred.MapTask: Finished spill 0
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting
- 14/12/14 17:05:28 INFO mapred.LocalJobRunner:
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000000_0' done.
- 14/12/14 17:05:28 INFO mapred.MapTask: io.sort.mb = 100
- 14/12/14 17:05:28 INFO mapred.MapTask: data buffer = 79691776/99614720
- 14/12/14 17:05:28 INFO mapred.MapTask: record buffer = 262144/327680
- 该行数据为:A 65
- 该行数据为:B 75
- 该行数据为:C 64
- 该行数据为:D 67
- 该行数据为:E 86
- 该行数据为:F 70
- 该行数据为:G 90
- 该行数据为:H 79
- 该行数据为:I 81
- 该行数据为:J 78
- 该行数据为:K 60
- 该行数据为:A 65
- 该行数据为:B 82
- 该行数据为:C 84
- 该行数据为:D 97
- 该行数据为:E 66
- 该行数据为:F 70
- 该行数据为:G 80
- 该行数据为:H 89
- 该行数据为:I 91
- 该行数据为:J 90
- 该行数据为:K 69
- 该行数据为:A 71
- 该行数据为:B 84
- 该行数据为:C 94
- 该行数据为:D 67
- 该行数据为:E 96
- 该行数据为:F 80
- 该行数据为:G 70
- 该行数据为:H 71
- 该行数据为:I 81
- 该行数据为:J 98
- 该行数据为:K 80
- 该行数据为:A 85
- 该行数据为:B 75
- 该行数据为:C 69
- 该行数据为:D 87
- 该行数据为:E 89
- 该行数据为:F 80
- 该行数据为:G 70
- 该行数据为:H 99
- 该行数据为:I 81
- 该行数据为:J 88
- 该行数据为:K 60
- 该行数据为:A 65
- 该行数据为:B 75
- 该行数据为:C 60
- 该行数据为:D 67
- 该行数据为:E 80
- 该行数据为:F 92
- 该行数据为:G 76
- 该行数据为:H 79
- 该行数据为:I 68
- 该行数据为:J 78
- 该行数据为:K 70
- 该行数据为:A 85
- 该行数据为:B 85
- 该行数据为:C 74
- 该行数据为:D 87
- 该行数据为:E 76
- 该行数据为:F 60
- 该行数据为:G 60
- 该行数据为:H 79
- 该行数据为:I 81
- 该行数据为:J 78
- 该行数据为:K 80
- 该行数据为:A 85
- 该行数据为:B 65
- 该行数据为:C 74
- 该行数据为:D 67
- 该行数据为:E 86
- 该行数据为:F 70
- 该行数据为:G 70
- 该行数据为:H 79
- 该行数据为:I 81
- 该行数据为:J 78
- 该行数据为:K 60
- 该行数据为:A 70
- 该行数据为:B 69
- 该行数据为:C 60
- 该行数据为:D 89
- 该行数据为:E 69
- 该行数据为:F 95
- 该行数据为:G 75
- 该行数据为:H 59
- 该行数据为:I 60
- 该行数据为:J 79
- 该行数据为:K 65
- 14/12/14 17:05:28 INFO mapred.MapTask: Starting flush of map output
- 第1个数值为:65
- 第2个数值为:65
- 第3个数值为:71
- 第4个数值为:85
- 第5个数值为:65
- 第6个数值为:85
- 第7个数值为:85
- 第8个数值为:70
- 第1个数值为:65
- 第2个数值为:84
- 第3个数值为:75
- 第4个数值为:85
- 第5个数值为:75
- 第6个数值为:69
- 第7个数值为:82
- 第8个数值为:75
- 第1个数值为:84
- 第2个数值为:69
- 第3个数值为:64
- 第4个数值为:74
- 第5个数值为:94
- 第6个数值为:60
- 第7个数值为:60
- 第8个数值为:74
- 第1个数值为:67
- 第2个数值为:87
- 第3个数值为:97
- 第4个数值为:67
- 第5个数值为:67
- 第6个数值为:87
- 第7个数值为:89
- 第8个数值为:67
- 第1个数值为:66
- 第2个数值为:86
- 第3个数值为:96
- 第4个数值为:86
- 第5个数值为:89
- 第6个数值为:69
- 第7个数值为:80
- 第8个数值为:76
- 第1个数值为:70
- 第2个数值为:95
- 第3个数值为:70
- 第4个数值为:70
- 第5个数值为:80
- 第6个数值为:60
- 第7个数值为:80
- 第8个数值为:92
- 第1个数值为:60
- 第2个数值为:76
- 第3个数值为:70
- 第4个数值为:70
- 第5个数值为:80
- 第6个数值为:90
- 第7个数值为:75
- 第8个数值为:70
- 第1个数值为:79
- 第2个数值为:89
- 第3个数值为:71
- 第4个数值为:99
- 第5个数值为:59
- 第6个数值为:79
- 第7个数值为:79
- 第8个数值为:79
- 第1个数值为:60
- 第2个数值为:81
- 第3个数值为:81
- 第4个数值为:81
- 第5个数值为:81
- 第6个数值为:91
- 第7个数值为:68
- 第8个数值为:81
- 第1个数值为:78
- 第2个数值为:79
- 第3个数值为:78
- 第4个数值为:88
- 第5个数值为:78
- 第6个数值为:98
- 第7个数值为:90
- 第8个数值为:78
- 第1个数值为:60
- 第2个数值为:70
- 第3个数值为:69
- 第4个数值为:60
- 第5个数值为:80
- 第6个数值为:65
- 第7个数值为:60
- 第8个数值为:80
- 14/12/14 17:05:28 INFO mapred.MapTask: Finished spill 0
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000001_0 is done. And is in the process of commiting
- 14/12/14 17:05:28 INFO mapred.LocalJobRunner:
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000001_0' done.
- 14/12/14 17:05:28 INFO mapred.LocalJobRunner:
- 14/12/14 17:05:28 INFO mapred.Merger: Merging 2 sorted segments
- 14/12/14 17:05:28 INFO mapred.Merger: Down to the last merge-pass, with 2 segments left of total size: 180 bytes
- 14/12/14 17:05:28 INFO mapred.LocalJobRunner:
- 第1个数值为:58
- 第2个数值为:73
- 第1个数值为:76
- 第2个数值为:57
- 第1个数值为:57
- 第2个数值为:72
- 第1个数值为:78
- 第2个数值为:66
- 第1个数值为:64
- 第2个数值为:81
- 第1个数值为:77
- 第2个数值为:69
- 第1个数值为:67
- 第2个数值为:73
- 第1个数值为:79
- 第2个数值为:66
- 第1个数值为:70
- 第2个数值为:78
- 第1个数值为:83
- 第2个数值为:67
- 第1个数值为:58
- 第2个数值为:68
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting
- 14/12/14 17:05:28 INFO mapred.LocalJobRunner:
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task attempt_local_0001_r_000000_0 is allowed to commit now
- 14/12/14 17:05:28 INFO output.FileOutputCommitter: Saved output of task 'attempt_local_0001_r_000000_0' to hdfs://hadoop:9000/user/hadoop/output4
- 14/12/14 17:05:28 INFO mapred.LocalJobRunner: reduce > reduce
- 14/12/14 17:05:28 INFO mapred.TaskRunner: Task 'attempt_local_0001_r_000000_0' done.
- 14/12/14 17:05:28 INFO mapred.JobClient: map 100% reduce 100%
- 14/12/14 17:05:28 INFO mapred.JobClient: Job complete: job_local_0001
- 14/12/14 17:05:28 INFO mapred.JobClient: Counters: 14
- 14/12/14 17:05:28 INFO mapred.JobClient: FileSystemCounters
- 14/12/14 17:05:28 INFO mapred.JobClient: FILE_BYTES_READ=50573
- 14/12/14 17:05:28 INFO mapred.JobClient: HDFS_BYTES_READ=2630
- 14/12/14 17:05:28 INFO mapred.JobClient: FILE_BYTES_WRITTEN=103046
- 14/12/14 17:05:28 INFO mapred.JobClient: HDFS_BYTES_WRITTEN=55
- 14/12/14 17:05:28 INFO mapred.JobClient: Map-Reduce Framework
- 14/12/14 17:05:28 INFO mapred.JobClient: Reduce input groups=11
- 14/12/14 17:05:28 INFO mapred.JobClient: Combine output records=22
- 14/12/14 17:05:28 INFO mapred.JobClient: Map input records=176
- 14/12/14 17:05:28 INFO mapred.JobClient: Reduce shuffle bytes=0
- 14/12/14 17:05:28 INFO mapred.JobClient: Reduce output records=11
- 14/12/14 17:05:28 INFO mapred.JobClient: Spilled Records=44
- 14/12/14 17:05:28 INFO mapred.JobClient: Map output bytes=1056
- 14/12/14 17:05:28 INFO mapred.JobClient: Combine input records=176
- 14/12/14 17:05:28 INFO mapred.JobClient: Map output records=176
- 14/12/14 17:05:28 INFO mapred.JobClient: Reduce input records=22
- 最终结果为:
- A 65
- B 66
- C 64
- D 72
- E 72
- F 73
- G 70
- H 72
- I 74
- J 75
- K 63
为了更清晰从将要执行的控制台中看到map和reduce过程的执行都进行了那些操作,我们在其中打印了相关信息,这里有自己的两点疑惑需要拿出来闹闹:
(1).这里我写的程序和书中不一样,没有增加StringTokenizer token = new StringTokenizer(line,"line")这行,事实上我加上去后会出现错误,我的理解是,因为默认格式是TextInputFormat即已经将文件中的文本按照行标示进行分割,即输入给map方法的已经是以一行为单位的记录,所以这里不需要以“\n”进行分割了。书中的做法应该是假定将整个文件拿过来,统一处理,但事实上这里默认的TextInputFormat已经完成了前期工作。(如果执迷不悟这样处理的话,距离来说NameScore1.txt中第一行是“A 55”整个以“\n”分割后就是一个整体了,再以“\t”就无法分割了。)
(2).从执行过程打印的信息,起初让我有些疑惑,因为从信息来看,似乎是:NameScore1.txt被分割并以每行记录进入map过程,当执行到该文件的最后一行记录时,从打印信息我们似乎看到的是紧接着就去执行reduce过程了,后面的NameScore2.txt也是如此,当两个文件都分别执行了map和reduce时似乎又执行了一次reduce操作。那么事实是不是如此,如果真是这样,这与先前所看到的理论中提到当map执行完后再执行reduce是否有冲突。
通过查看代码我们发现
job.setMapperClass(MapperClass.class);
job.setCombinerClass(ReducerClass.class);
job.setReducerClass(ReducerClass.class);
是的,没错,在这里我们发现了端倪,其真正执行过程是:先执行map,这就是过程信息中打印了每条成绩记录,后面执行的reduce其实是介于map和reduce之间的combiner操作,那么这个Combiner类又为何物,通过神奇的API我们可以发现Combine其实就是一次reduce过程,而且这里明确写了combiner和reduce过程都是使用ReducerClass类,从而就出现了这里为什么对于单个文件都会先执行map然后在reduce,再后来是两个map执行后,才执行的真正的reduce。
2.去重——阉割版的WordCount
相比于前面的求平均值例子需要添加一些逻辑代码来说,这里的去重更像是阉割版的WordCount。
如果你还是用传统的思维在考量一个去重的程序需要多少次的判断,如果你还不了解什么是真正的map和reduce。Hadoop中的去重问题被你整复杂了。要知道,当一个map执行完后会对执行的数据进行一个排序,比如按照字母先后顺序;后面会进入combine阶段,这阶段主要是针对key-value中有相同的key就合并;再到reduce阶段,通过迭代器遍历前一阶段合并的各个元素,得到最终的输出结果。
对于去重来说,我们不在乎一个元素到底出现了几次,只要知道这个元素确实出现了,并能够再最后显示出来就行了,通过map和combiner,我们最终得到的key-value对中的key都是不一样的,也就是说在完成合并的同时就是我们所需要的去重操作(是不是有点绕)。最终reduce输出的就是具有唯一性的去重的元素集合。我们还是按照理清map和reduce的思路来看待这个去重问题:
map: 数据中的一行记录如"(安徽 jack)"——输入数据
直接以key-value的方式写入上下文对象context(这里的value并不是我们关心的对象,可以为空)——执行功能
得到指定键值对类型的输出如"(new Text(安徽),new Text(""))"——输出结果
reduce: map的输出——输入数据
直接以key-value的方式写入上下文对象context(同样,value并不是我们关心的对象)——执行功能
得到指定键值对类型的输入——输出结果
鉴于以上对于map和reduce的理解,代码如下:
- package org.apache.mapreduce;
- import java.io.IOException;
- import java.util.Collection;
- import java.util.Iterator;
- import java.util.StringTokenizer;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapred.TextInputFormat;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.Mapper;
- import org.apache.hadoop.mapreduce.Reducer;
- import org.apache.hadoop.util.GenericOptionsParser;
- import org.apache.mapreduce.Test1123.MapperClass;
- import org.apache.mapreduce.Test1123.ReducerClass;
- public class Test1215 {
- public static class MapperClass extends Mapper<LongWritable, Text, Text, Text> {
- public void map(LongWritable key, Text value, Context context){
- try {
- context.write(value, new Text(""));
- System.out.println("value:" + value);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public static class ReducerClass extends Reducer<Text, Text, Text, Text>{
- public void reduce(Text key, Iterable<Text> value, Context context){
- try {
- context.write(key, new Text(""));
- System.out.println("key:"+key);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * @param args
- * @throws IOException
- * @throws ClassNotFoundException
- * @throws InterruptedException
- */
- public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
- Configuration conf = new Configuration();
- String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
- if (otherArgs.length != 2) {
- System.err.println("Usage: wordcount <in> <out>");
- System.exit(2);
- }
- Job job = new Job(conf, "Test1214");
- job.setJarByClass(Test1215.class);
- job.setMapperClass(MapperClass.class);
- job.setCombinerClass(ReducerClass.class);
- job.setReducerClass(ReducerClass.class);
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(Text.class);
- org.apache.hadoop.mapreduce.lib.input.FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
- org.apache.hadoop.mapreduce.lib.output.FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
- System.exit(job.waitForCompletion(true) ? 0 : 1);
- System.out.println("end");
- }
- }
数据集:手动创建两个文件,每个文件内都有重复元素,两个文件内也有重复元素,具体如下:
repeat1.txt:
- 安徽 jack
- 江苏 jim
- 江西 lucy
- 广东 david
- 上海 smith
- 安徽 jack
- 江苏 jim
- 北京 yemener
repeat2.txt
- 江西 lucy
- 安徽 jack
- 上海 hanmei
- 北京 yemener
- 新疆 afanti
- 黑龙江 lily
- 福建 tom
- 安徽 jack
通过运行,我们发现控制台打印的信息如下:
- 14/12/15 21:57:07 INFO jvm.JvmMetrics: Initializing JVM Metrics with processName=JobTracker, sessionId=
- 14/12/15 21:57:07 WARN mapred.JobClient: No job jar file set. User classes may not be found. See JobConf(Class) or JobConf#setJar(String).
- 14/12/15 21:57:07 INFO input.FileInputFormat: Total input paths to process : 2
- 14/12/15 21:57:07 INFO mapred.JobClient: Running job: job_local_0001
- 14/12/15 21:57:07 INFO input.FileInputFormat: Total input paths to process : 2
- 14/12/15 21:57:07 INFO mapred.MapTask: io.sort.mb = 100
- 14/12/15 21:57:07 INFO mapred.MapTask: data buffer = 79691776/99614720
- 14/12/15 21:57:07 INFO mapred.MapTask: record buffer = 262144/327680
- value:安徽 jack
- value:江苏 jim
- value:江西 lucy
- value:广东 david
- value:上海 smith
- value:安徽 jack
- value:江苏 jim
- value:北京 yemener
- 14/12/15 21:57:08 INFO mapred.MapTask: Starting flush of map output
- key:上海 smith
- key:北京 yemener
- key:安徽 jack
- key:广东 david
- key:江苏 jim
- key:江西 lucy
- 14/12/15 21:57:08 INFO mapred.MapTask: Finished spill 0
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting
- 14/12/15 21:57:08 INFO mapred.LocalJobRunner:
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000000_0' done.
- 14/12/15 21:57:08 INFO mapred.MapTask: io.sort.mb = 100
- 14/12/15 21:57:08 INFO mapred.MapTask: data buffer = 79691776/99614720
- 14/12/15 21:57:08 INFO mapred.MapTask: record buffer = 262144/327680
- value:江西 lucy
- value:安徽 jack
- value:上海 hanmei
- value:北京 yemener
- value:新疆 afanti
- value:黑龙江 lily
- value:福建 tom
- value:安徽 jack
- 14/12/15 21:57:08 INFO mapred.MapTask: Starting flush of map output
- key:上海 hanmei
- key:北京 yemener
- key:安徽 jack
- key:新疆 afanti
- key:江西 lucy
- key:福建 tom
- key:黑龙江 lily
- 14/12/15 21:57:08 INFO mapred.MapTask: Finished spill 0
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000001_0 is done. And is in the process of commiting
- 14/12/15 21:57:08 INFO mapred.LocalJobRunner:
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000001_0' done.
- 14/12/15 21:57:08 INFO mapred.LocalJobRunner:
- 14/12/15 21:57:08 INFO mapred.Merger: Merging 2 sorted segments
- 14/12/15 21:57:08 INFO mapred.Merger: Down to the last merge-pass, with 2 segments left of total size: 212 bytes
- 14/12/15 21:57:08 INFO mapred.LocalJobRunner:
- key:上海 hanmei
- key:上海 smith
- key:北京 yemener
- key:安徽 jack
- key:广东 david
- key:新疆 afanti
- key:江苏 jim
- key:江西 lucy
- key:福建 tom
- key:黑龙江 lily
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting
- 14/12/15 21:57:08 INFO mapred.LocalJobRunner:
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task attempt_local_0001_r_000000_0 is allowed to commit now
- 14/12/15 21:57:08 INFO output.FileOutputCommitter: Saved output of task 'attempt_local_0001_r_000000_0' to hdfs://hadoop:9000/user/hadoop/output5
- 14/12/15 21:57:08 INFO mapred.LocalJobRunner: reduce > reduce
- 14/12/15 21:57:08 INFO mapred.TaskRunner: Task 'attempt_local_0001_r_000000_0' done.
- 14/12/15 21:57:08 INFO mapred.JobClient: map 100% reduce 100%
- 14/12/15 21:57:08 INFO mapred.JobClient: Job complete: job_local_0001
- 14/12/15 21:57:08 INFO mapred.JobClient: Counters: 14
- 14/12/15 21:57:08 INFO mapred.JobClient: FileSystemCounters
- 14/12/15 21:57:08 INFO mapred.JobClient: FILE_BYTES_READ=50584
- 14/12/15 21:57:08 INFO mapred.JobClient: HDFS_BYTES_READ=507
- 14/12/15 21:57:08 INFO mapred.JobClient: FILE_BYTES_WRITTEN=102997
- 14/12/15 21:57:08 INFO mapred.JobClient: HDFS_BYTES_WRITTEN=140
- 14/12/15 21:57:08 INFO mapred.JobClient: Map-Reduce Framework
- 14/12/15 21:57:08 INFO mapred.JobClient: Reduce input groups=10
- 14/12/15 21:57:08 INFO mapred.JobClient: Combine output records=13
- 14/12/15 21:57:08 INFO mapred.JobClient: Map input records=16
- 14/12/15 21:57:08 INFO mapred.JobClient: Reduce shuffle bytes=0
- 14/12/15 21:57:08 INFO mapred.JobClient: Reduce output records=10
- 14/12/15 21:57:08 INFO mapred.JobClient: Spilled Records=26
- 14/12/15 21:57:08 INFO mapred.JobClient: Map output bytes=220
- 14/12/15 21:57:08 INFO mapred.JobClient: Combine input records=16
- 14/12/15 21:57:08 INFO mapred.JobClient: Map output records=16
- 14/12/15 21:57:08 INFO mapred.JobClient: Reduce input records=13
基于以上两个例子的分析,让我们了解map是怎么一回事,reduce又做了什么,在map和reduce之间还有那些猫腻,整个mapreduce是如何一次次的帮助我们完成我们想要的逻辑,当然这里为了方便用的是小数据集,事实上在大数据集上解决这样的问题更能凸显mapreduce高大上和救世主的形象。
真心觉得Doug Cutting很牛,如何写出这样的框架,低头一想,前面的路还很长。今天就到这,觉得有用,记得点赞哦,你的到来是对我最大的鼓舞^_^
本文链接:《Hadoop阅读笔记(二)——利用MapReduce求平均数和去重》http://www.cnblogs.com/bigdataZJ/p/hadoopreading2.html
友情赞助
如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。
1. 支付宝 2. 微信
Hadoop阅读笔记(二)——利用MapReduce求平均数和去重的更多相关文章
- Hadoop阅读笔记(三)——深入MapReduce排序和单表连接
继上篇了解了使用MapReduce计算平均数以及去重后,我们再来一探MapReduce在排序以及单表关联上的处理方法.在MapReduce系列的第一篇就有说过,MapReduce不仅是一种分布式的计算 ...
- Hadoop阅读笔记(一)——强大的MapReduce
前言:来园子已经有8个月了,当初入园凭着满腔热血和一脑门子冲动,给自己起了个响亮的旗号“大数据 小世界”,顿时有了种世界都是我的,世界都在我手中的赶脚.可是......时光飞逝,岁月如梭~~~随手一翻 ...
- Hadoop阅读笔记(四)——一幅图看透MapReduce机制
时至今日,已然看到第十章,似乎越是焦躁什么时候能翻完这本圣经的时候也让自己变得更加浮躁,想想后面还有一半的行程没走,我觉得这样“有口无心”的学习方式是不奏效的,或者是收效甚微的.如果有幸能有大牛路过, ...
- Hadoop阅读笔记(七)——代理模式
关于Hadoop已经小记了六篇,<Hadoop实战>也已经翻完7章.仔细想想,这么好的一个框架,不能只是流于应用层面,跑跑数据排序.单表链接等,想得其精髓,还需深入内部. 按照<Ha ...
- Hadoop阅读笔记(五)——重返Hadoop目录结构
常言道:男人是视觉动物.我觉得不完全对,我的理解是范围再扩大点,不管男人女人都是视觉动物.某些场合(比如面试.初次见面等),别人没有那么多的闲暇时间听你诉说过往以塑立一个关于你的完整模型.所以,第一眼 ...
- 利用MapReduce计算平均数
利用mapreduce求出股票价格的开盘和收盘平均数 下图为采集到的股票信息,共计1416支股票的信息 因为在linux系统下默认采用utf-8的编码格式,而在win下txt默认采用ANSI编码格式. ...
- Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable
酒,是个好东西,前提要适量.今天参加了公司的年会,主题就是吃.喝.吹,除了那些天生话唠外,大部分人需要加点酒来作催化剂,让一个平时沉默寡言的码农也能成为一个喷子!在大家推杯换盏之际,难免一些画面浮现脑 ...
- 《Java编程思想》阅读笔记二
Java编程思想 这是一个通过对<Java编程思想>(Think in java)进行阅读同时对java内容查漏补缺的系列.一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会忽略或 ...
- Java Jdk1.8 HashMap源代码阅读笔记二
三.源代码阅读 3.元素包括containsKey(Object key) /** * Returns <tt>true</tt> if this map contains a ...
随机推荐
- 伪元素小tips
1.伪元素的是dom看不见的,表现为行内元素.我这里说的伪元素是指::before ::after.其他的像:first-letter :visited 则属于伪类. 2.因为dom不可见,所以伪元素 ...
- XCAT在虚拟机上部署系统
xcat更新到2.13了,老的manual很多都没用了.前一整子居然没搞成功,只好再来试一次. 官网也搬到了xcat.org.首先就是下core和dep包 分别是 xCAT Core Packages ...
- C++混合编程之idlcpp教程Python篇(9)
上一篇在这 C++混合编程之idlcpp教程Python篇(8) 第一篇在这 C++混合编程之idlcpp教程(一) 与前面的工程相比,工程PythonTutorial7中除了四个文件PythonTu ...
- SQLSERVER的一个不显眼的功能 备份文件的分割
SQLSERVER的一个不显眼的功能 备份文件的分割 当完整备份数据库的时候,我们有时候可能会遇到一种极端情况,比如服务器上C,D,E三个盘符都只剩下5G空间了 但是如果要完整备份业务库需要12G的空 ...
- 谈谈eclipse使用技巧一
俗话说的好啊,“工于利启事,必先善其器”,如果说你的编程功底是一个枪法的话,那么强大的eclipse就是android战士们最好的武器. 这里,我们来总结eclipse的使用技巧,从而使我们的编程达到 ...
- Xamarin开发Android笔记:使用ZXing进行连续扫描
在项目开发中需要使用到条码扫描,因为以前就测试过ZXing,感觉识别速度和功能都不错,所以直接引用.不过在实际开发的过程中,却遇到连续扫描的问题,每次扫描识别完成之后,扫描窗体自动关闭了. 在Xama ...
- 软件工程day4
使用ps制作了一个icon,将在下个版本中添加,用作程序图标. 参与组例会,得知新功能“吐槽墙”将以聊天室类似的社区形式实现. 提出对现有UI的建议: 对目前的登录窗口的UI不做改动,将标题的“用户登 ...
- 解决IE6下png图片透明度不显示的问题
世界上最遥远的距离,不外乎我在搞前端,你却在用旧IE,现在随着XP要退休了,IE6的市场占有率应该也会逐步下滑.不过基于天朝人民的惰性以及企鹅微软的“扎篱笆”活动,做网站的朋友依旧不能忽视IE6的存在 ...
- [FPGA] 1、开发板使用和引脚连接
目录 1.注意事项 2.设备简介 3.引脚分配 注意事项: ① 插拔下载线时必须断电! ② Quartus II 软件和 NIOS 软件的版本必须一致,并安装在同一个目录下面,安装目录不要有中文和空格 ...
- IOS 公共类-数字处理
1.写一个方法,调用的时候交换两个数的值 -(void) swap:(int*)a andNumber:(int*)b{ int temp = *a; *a = *b; *b = temp; } 调用 ...