14 - MapReduce之数据清洗(ETL)案例,倒排索引案例,ReduceTask 工作机制,Hadoop 数据压缩简介

一:简介

             在运行核心业务 Mapreduce 程序之前,往往要先对数据进行清洗,清理掉不符合用户要求的数据。
      清理的过程往往只需要运行 mapper 程序,不需要运行 reduce 程序。

二:日志清洗案例之简单解析版

  1.  需求:去除日志中字段长度小于等于11的日志(每一行按照空格切割,切割后数组长度小于11的日志不要)
  2.  数据如下:
      
  3. 代码实现如下:
     ⑴创建mapper类:
             
    package com.kgf.mapreduce.weblog;
    
    import java.io.IOException;
    
    import org.apache.hadoop.io.LongWritable;
    import org.apache.hadoop.io.NullWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Mapper;
    
    public class LogMapper extends Mapper<LongWritable, Text, Text, NullWritable> {
    
    	Text k = new Text();
    	
    	@Override
    	protected void map(LongWritable key, Text value,Context context)
    			throws IOException, InterruptedException {
    		
    		//1:获取一行
    		String line = value.toString();
    		
    		//2:解析一行数据
    		boolean result = parseLog(line);
    		
    		if(!result) {
    			return;
    		}
    		k.set(line);
    		context.write(k, NullWritable.get());
    	}
    
    	private boolean parseLog(String line) {
    		String[] fields = line.split(" ");
    		if(fields.length>11) {
    			return true;
    		}
    		return false;
    	}
    	
    }
    

    ⑵创建Driver:

    package com.kgf.mapreduce.weblog;
    
    import java.io.IOException;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.NullWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Job;
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    
    public class LogDriver {
    
    	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
    		//1:获取Job对象
    		Configuration conf = new Configuration();
    		Job job = Job.getInstance(conf);
    		
    		//2:设置jar对象
    		job.setJarByClass(LogDriver.class);
    		
    		//3:设置关联Mapper
    		job.setMapperClass(LogMapper.class);
    		
    		//4:设置mapper输出类型
    		job.setMapOutputKeyClass(Text.class);
    		job.setMapOutputValueClass(NullWritable.class);
    		
    		//5:设置reduce task为0
    		job.setNumReduceTasks(0);
    		
    		//6:设置最终输出参数
    		job.setOutputKeyClass(Text.class);
    		job.setOutputValueClass(NullWritable.class);
    		
    		//7:设置文件输入输出路径
    		FileInputFormat.setInputPaths(job, new Path(args[0]));
    		FileOutputFormat.setOutputPath(job, new Path(args[1]));
    		
    		//8:提交
    		boolean result = job.waitForCompletion(true);
    		System.exit(result?0:1);
    	}
    	
    }
    

  4.   效果:
     ⑴日志清理前数据有如下数量:
                    
      ⑵清理后:
               
      ⑶程序运行日志如下:
              
  5. 计数器应用 
     ⑴简介:
               Hadoop 为每个作业维护若干内置计数器,以描述多项指标。例如,某些计数器记录已
         处理的字节数和记录数,使用户可监控已处理的输入数据量和已产生的输出数据量。 
     ⑵计数器方式如下:
             
     ⑶案例,我们可以在上面的日志清洗案例中加上计数器组应用:代码如下:
            
         效果如下(可以发现控制台出现了下面的日志内容,可以帮助我们定位问题):
            

三:倒排索引案例

  1. 现在有三个文件a.txt,b.txt,c.txt,现在我们需要将文件里面的单词汇总
    ⑴文件如下:
            
    ⑵最后要求的结果(汇总每个单词所在的文件中个数):
          
  2.  代码实现如下:
     ⑴建立bean对象
           
    package com.kgf.mapreduce.index;
    
    import java.io.DataInput;
    import java.io.DataOutput;
    import java.io.IOException;
    
    import org.apache.hadoop.io.Writable;
    
    public class IndexVo implements Writable{
    	
    	private String word;
    	
    	private String file;
    	
    	private int num;
    	
    	public IndexVo(String word, String file, int num) {
    		super();
    		this.word = word;
    		this.file = file;
    		this.num = num;
    	}
    
    	public IndexVo() {
    		super();
    	}
    
    	@Override
    	public void readFields(DataInput in) throws IOException {
    		this.word = in.readUTF();
    		this.file = in.readUTF();
    		this.num = in.readInt();
    	}
    
    	@Override
    	public void write(DataOutput out) throws IOException {
    		out.writeUTF(word);
    		out.writeUTF(file);
    		out.writeInt(num);
    	}
    
    	public String getWord() {
    		return word;
    	}
    
    	public void setWord(String word) {
    		this.word = word;
    	}
    
    	public String getFile() {
    		return file;
    	}
    
    	public void setFile(String file) {
    		this.file = file;
    	}
    
    	public int getNum() {
    		return num;
    	}
    
    	public void setNum(int num) {
    		this.num = num;
    	}
    
    	@Override
    	public String toString() {
    		return word + "\t" + file + "\t" + num;
    	}
    }
    
     ⑵建立mapper对象
           
    package com.kgf.mapreduce.index;
    
    import java.io.IOException;
    
    import org.apache.hadoop.io.LongWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Mapper;
    import org.apache.hadoop.mapreduce.lib.input.FileSplit;
    
    public class IndexMapper extends Mapper<LongWritable, Text, Text, IndexVo>{
    
    	private String fileName = null;
    	
    	Text k = new Text();
    	
    	IndexVo v = new IndexVo();
    	
    	@Override
    	protected void setup(Context context)
    			throws IOException, InterruptedException {
    		//1:获取文件切片信息
    		FileSplit splitFile = (FileSplit) context.getInputSplit();
    		fileName = splitFile.getPath().getName();
    	}
    	
    	@Override
    	protected void map(LongWritable key, Text value,Context context)
    			throws IOException, InterruptedException {
    		
    		//1:获取一行数据
    		String line = value.toString();
    		//2:切割数据
    		String[] fields = line.split("\t");
    		for (String field : fields) {
    			k.set(field);
    			v.setWord(field);
    			v.setFile(fileName);
    			v.setNum(1);
    			context.write(k, v);
    		}
    	}
    	
    }
    
    ⑶建立reducer对象
         
    package com.kgf.mapreduce.index;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.hadoop.io.NullWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Reducer;
    
    public class IndexReducer extends Reducer<Text, IndexVo, Text, NullWritable> {
    
    	
    	Text k = new Text();
    	
    	@Override
    	protected void reduce(Text key, Iterable<IndexVo> values,Context context)
    			throws IOException, InterruptedException {
    		
    		Map<String,Integer> map = new HashMap<String,Integer>();
    		
    		for (IndexVo indexVo : values) {
    			String name = indexVo.getFile();
    			if(map.containsKey(name)) {
    				map.put(name, map.get(name)+indexVo.getNum());
    			}else {
    				map.put(name, indexVo.getNum());
    			}
    		}
    		
    		String result = key.toString()+"\t";
    		for (String fileName : map.keySet()) {
    			result+=(fileName+"\t"+map.get(fileName))+"\t";
    		}
    		k.set(result);
    		context.write(k, NullWritable.get());
    	}
    }
    
    ⑷建立Driver对象
         
    package com.kgf.mapreduce.index;
    
    import java.io.IOException;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.NullWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Job;
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    
    public class IndexDriver {
    
    	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
    		//1:获取job对象
    		Configuration conf = new Configuration();
    		Job job = Job.getInstance(conf);
    		
    		//2:设置jar
    		job.setJarByClass(IndexDriver.class);
    		
    		//3:关联mapper和reducer
    		job.setMapperClass(IndexMapper.class);
    		job.setReducerClass(IndexReducer.class);
    		
    		//4:设置mapper输出参数
    		job.setMapOutputKeyClass(Text.class);
    		job.setMapOutputValueClass(IndexVo.class);
    		
    		//5:设置最终输出参数
    		job.setOutputKeyClass(Text.class);
    		job.setOutputValueClass(NullWritable.class);
    		
    		//6:设置数据输入输出路径
    		FileInputFormat.setInputPaths(job, new Path(args[0]));
    		FileOutputFormat.setOutputPath(job, new Path(args[1]));
    		
    		//7:提交
    		boolean rsult = job.waitForCompletion(true);
    		System.exit(rsult?0:1);
    	}
    	
    }
    
            

四: ReduceTask 工作机制 

  1. ReduceTask有如下特点:
     
  2.  流程图如下:
     
     ⑴Copy阶段:
             ReduceTask 从各个 MapTask 上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,
        则写到磁盘上,否则直接放到内存中。
     ⑵Merge 阶段:
              在远程拷贝数据的同时,ReduceTask 启动了两个后台线程对内存和磁盘上的文件进行合并,
        以防止内存使用过多或磁盘上文件过多。 
     ⑶Sort 阶段:
              按照 MapReduce 语义,用户编写 reduce()函数输入数据是按 key 进行聚集的一组数据。
        为了将 key 相同的数据聚在一起,Hadoop 采用了基于排序的策略。由于各个 MapTask 已经
        实现对自己的处理结果进行了局部排序,因此,ReduceTask 只需对所有数据进行一次归并排序即可。 

     ⑷Reduce 阶段:
               reduce()函数将计算结果写到 HDFS 上。 

五: Hadoop 数据压缩 

  1. 简介
        压缩技术能够有效减少底层存储系统(HDFS)读写字节数。压缩提高了网络带宽和磁盘空间的效率。
    在 Hadoop 下,尤其是数据规模很大和工作负载密集的情况下,使用数据压缩显得非常重要。在这种情况下,
    I/O 操作和网络数据传输要花大量的时间。还有,Shuffle与 Merge 过程同样也面临着巨大的 I/O 压力。
        鉴于磁盘 I/O 和网络带宽是 Hadoop 的宝贵资源,数据压缩对于节省资源、最小化磁盘
    I/O 和网络传输非常有帮助。不过,尽管压缩与解压操作的 CPU 开销不高,其性能的提升和
    资源的节省并非没有代价。 
        如果磁盘 I/O 和网络带宽影响了 MapReduce 作业性能,在任意 MapReduce 阶段启用压
    缩都可以改善端到端处理时间并减少 I/O 和网络流量。 
         压缩 Mapreduce 的一种优化策略:通过压缩编码对 Mapper 或者 Reducer 的输出进行
    压缩,以减少磁盘 IO,提高 MR 程序运行速度(但相应增加了 cpu 运算负担)。 
         
  2. MR支持的压缩编码
        
       
  3.  压缩性能的比较 
       
  4.  压缩方式选择    
      ⑴Gzip 压缩 
             
      ⑵ Bzip2 压缩 
             
       ⑶Lzo 压缩 
             
             
       ⑷Snappy 压缩 
             
  5. 压缩位置选择 
     压缩可以在 MapReduce 作用的任意阶段启用。 
     
  6. 压缩配置参数 
     
     
     
     
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值