Hadoop复习(2)MapReduce

核心思想(理解)

工作流程(理解)

MapReduce 的 Shuffle 过程


Map端

1.写入缓存区,map函数的输出会先写入一个缓冲区,默认大小为100mb

2.溢写
一旦缓存内容达到阈值(默认为80%),一个后台线程便开始吧内容溢写入磁盘.

3.分区
在写磁盘前,首先根据数据划分相应的分区,如果有一个Combiner,它就在排序后的输出运行.

4.合并,排序
每次内容缓冲区达到溢出阈值,就会新建一个溢出文件(Spill File),以此在Map任务写完前,可能会有多个溢出文件.在任务完成之前,溢出文件被合并成一个已分区且已排序的输出文件。

注意点:
排序结束后,用户可根据实际需求选择是否要执行合并操作,只有预先定义了 Combine()函数,才会执行合并操作即合并操作并不是必须的
排序默认按key排序

最后一步:归并(merge)

整体流程图:注意这里merge应该改成归并

Rdeuce端


MapReduce 体系结构主要包含哪四个部分,这四个部分的主要作用

简单代码分析

词频统计

Mapper函数:

public static class WordCountMapper extends
Mapper<LongWritable,Text,Text,LongWritable> {//对应v1,k1,v2,k2类型
//通常来说v1规定为LongWritable k1,v2,k2则需要根据题目去规定类型 
@Override
	public void map(LongWritable key, Text value, Context context) throws
IOException, InterruptedException {
	String line = value.toString(); //获取value中的值
	String[] split = line.split(","); //分词 通过逗号分割单词 
	//如果输入的是 hello,jie  那么就返回一个数组内容是 [hello] [jie] 
	for (String word : split) {
		context.write(new Text(word),new LongWritable(1)); //输出
		//构造出一种映射关系
		//如上面的例子最后结果就是 {hello,1} {jie,1}
		//这样后续吧 key 相同的  value加上去就是词频统计 
	}
}

Reducer函数:

public static class WordCountReducer extends Reducer<Text,LongWritable,Text,LongWritable>//对应k3,v3 k4,v4 
{
/**
* 自定义我们的reduce逻辑
* 所有的key都是我们的单词,所有的values都是我们单词出现的次数
* @param key ,@param values ,@param context
*/
@Override
	protected void reduce(Text key, Iterable<LongWritable> values,//这里Iterable<LongWritable>类型 是因为map后经过shuffle过程的归并
Context context) throws IOException, InterruptedException {//比如出现了3次jie 那么在map后会有3个{jie,1},shuffle会将key相同的进行归并
														//最后变成{jie,1,1,1} 我们只需要遍历循环加上value就完成词频统计了
	long count = 0;
	for (LongWritable value : values) {
		count += value.get(); //统计
	}
	context.write(key,new LongWritable(count));
	}
}

大数据例题


请编写程序实现统计出搜索关键词出现次数大于7000次的搜索关键词。
其实就是磁频统计,在reduce阶段计算出次数,如果超过7000才写入context
map函数:

public static class TokenizerMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
        public TokenizerMapper() {
        }
        public void map(LongWritable key, Text value, Mapper< LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException
        {
            String data=value.toString();
            String[] words=data.split("\\s+");
                //获取关键词 \\s+代表分割一到多个空格
				//数组下标为2时对应关键词
            context.write(new Text(words[2]), new IntWritable(1));
        }
    }

reduce函数:

	public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
        public IntSumReducer() {
        }
        public void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException
        {
        	int total=0;
        	for(IntWritable v:values) {
        		total+=v.get();
        	}
        	if(total>7000) {
        		context.write(key, new IntWritable(total));
        	}
        		
        }
    }
}

请编写程序实现获取URL排名第二、用户点击顺序第一的日志
这个比较简单就不说了。

薪水分区


满足要求就必须去自定义一个分区规则
map函数:

public static class TokenizerMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
        public TokenizerMapper() {
        }
		//这一大串就是写一个判断有无奖金方法 不是重点
        public static boolean isNumeric(final String str) {
        	 
            // null or empty
            if (str == null || str.length() == 0) {
              return false;
            }
         
            try {
              Integer.parseInt(str);
              return true;
            } catch (NumberFormatException e) {
         
              try {
                Double.parseDouble(str);
                return true;
         
              } catch (NumberFormatException ex) {
         
                try {
                  Float.parseFloat(str);
                  return true;
                } catch (NumberFormatException exx) {
                  return false;
                }
              }
            }
          }
        @Override
        public void map(LongWritable key, Text value, Mapper< LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException
        {
            String data=value.toString();
            String[] words=data.split(",");
                //获取关键词
            int temp=0;
            if(isNumeric(words[6])) {//判断有无奖金
            	temp=Integer.parseInt(words[6]);
            }	
            System.out.println(temp);
			//建立 员工姓名和金钱对应关系
            context.write(new Text(words[1]), new IntWritable(Integer.parseInt(words[5])+temp));
        }
    }

reduce函数 老老实实吧value加上去就好 没什么要干的

public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
        public IntSumReducer() {
        }
        @Override
        public void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException
        {
        	int total=0;
        	for(IntWritable v:values) {
        		total+=v.get();
        	}
        		context.write(key, new IntWritable(total));
        		
        }
    }
}

分区规则,记住语法怎么写就好

Partitioner<Text,IntWritable>{
    public int getPartition(Text key,IntWritable value,int
    numPartitions) {
    final int money=value.get();
    if(money<1500)
    	return 0%numPartitions;
    else if(money>=1500 && money<3000) 
    	return 1%numPartitions;
    else 
    	return 2%numPartitions;
    	}
    }

比较复杂的题目(重要在于理解shuffle过程)

文件合并和去重操作



思路:
啥都不用干! 吧文本内容当成key,value设置成空
shuffle过程的归并会吧key相同的value归并起来 这里我们设置value为空 这样就实现了去重操作
map函数

public static class TokenizerMapper extends Mapper<LongWritable, Text, Text, NullWritable> {
        public TokenizerMapper() {
        }
        @Override
        public void map(LongWritable key, Text value, Mapper< LongWritable, Text, Text, NullWritable>.Context context) throws IOException, InterruptedException
        {

            context.write(value, NullWritable.get());
        }
    }

reduce函数

public static class IntSumReducer extends Reducer<Text, NullWritable, Text, NullWritable> {
        public IntSumReducer() {
        }
        @Override
        public void reduce(Text key, Iterable<NullWritable> values,Context context) throws IOException, InterruptedException
        {
        	
        		context.write(key,NullWritable.get());
        }
    }
}

文件的排序



shuffle过程会根据key进行排序,所以吧文本的数字提取出来当做key就实现了排序
map函数

public static class TokenizerMapper extends Mapper<LongWritable, Text, IntWritable, Text> {
        public TokenizerMapper() {
        }
        private Text val = new Text("");//和value设置成null 本质一样的操作 
        @Override
    	protected void map(LongWritable key, Text value,Context context)
    			throws IOException, InterruptedException {
    		String line = value.toString();
    		if(line.trim().length()>0){
    			context.write(new IntWritable(Integer.valueOf(line.trim())), val);
    		}
    	}
    }

reduce函数
设置num为1 然后每次写入num+1 就实现了要求的前面那个数字

public static class IntSumReducer extends Reducer<IntWritable, Text, IntWritable, IntWritable> {
        public IntSumReducer() {
        }

        private IntWritable num = new IntWritable(1);
        @Override
    	public void reduce(IntWritable key, Iterable<Text> values,Context context)
    			throws IOException, InterruptedException {	
    		for(Text val:values){
    			context.write(num, key);
    			num = new IntWritable(num.get()+1);
    		}
    		
    	}
    }
}
posted @ 2021-06-29 14:53  一个经常掉线的人  阅读(274)  评论(0)    收藏  举报