8)Hadoop之MapReduce(计数器应用,数据清洗(ETL)案例)

本文详细介绍了使用Hadoop计数器监控数据处理情况,并通过MapReduce程序实现日志数据清洗,去除不符合要求的数据。包括计数器的使用方法,以及针对日志数据的具体清洗案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计数器应用
  • 概述:
    Hadoop为每个作业维护若干计数器,以描述多项指标。例如:某些计数器记录着已处理的字节数和记录数,使用户可以监控已处理的输入数据量和已经产生的输入数据量。
  • 计数器API:
    • 采用枚举的方式统计计数
      enum MyCounter{MALFORORMED,NORMAL}
      context.getCounter(MyCounter.MALFORORMED).increment(1)//对枚举定义的自定义计数器加1;
    • 采用计数器组,计数器名称的方式统计:
      context.getCounter("counterGroup","counter").increment(1);
    • 计数结果在程序运行后的控制台上查看
数据清洗(ETL)
  • 在运行核心业务MapReduce程序之前,往往要先对数据进行清洗,清理掉不符合用户要求的数据。清理的过程往往只需要运行Mapper程序,不需要运行Reduce程序

  • 数据清洗案例实操(简单解析版)

    • 需求:
      去除日志中字段长度小于等于11的日志。
    • 需求分析:
      需要在Map阶段对输入的数据根据规则进行过滤清洗。
    • 代码实现
      ①编写LogMapper类
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 获取1行数据
      String line = value.toString();
  
      // 2 解析日志
      boolean result = parseLog(line,context);
  
      // 3 日志不合法退出
      if (!result) {
       return;
      }
  
      // 4 设置key
      k.set(line);
  
      // 5 写出数据
      context.write(k, NullWritable.get());
     }
   // 2 解析日志
   private boolean parseLog(String line, Context context) {
 	// 1 截取
  	String[] fields = line.split(" "); 
  	// 2 日志长度大于11的为合法
  	if (fields.length > 11) {
   	    // 系统计数器
   	    context.getCounter("map", "true").increment(1);
   	    return true;
  	}else{
   	    context.getCounter("map", "false").increment(1);
   	return false;
        }
   }
}

②编写LogDriver类

public class LogDriver {
	public static void main(String[] args) throws Exception {
		// 输入输出路径需要根据自己电脑上实际的输入输出路径设置
		args = new String[] { "e:/input/inputlog", "e:/output1" };
		
		// 1 获取job信息
  		Configuration conf = new Configuration();
  		Job job = Job.getInstance(conf);
		
		// 2 加载jar包
 		job.setJarByClass(LogDriver.class);
		
		// 3 关联map
  		job.setMapperClass(LogMapper.class);	

		// 4 设置最终输出类型
  		job.setOutputKeyClass(Text.class);
  		job.setOutputValueClass(NullWritable.class);
		
		// 设置reducetask个数为0
  		job.setNumReduceTasks(0);
		
		// 5 设置输入和输出路径
  		FileInputFormat.setInputPaths(job, new Path(args[0]));
  		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		// 6 提交
  		job.waitForCompletion(true);
 	}
}
  • 数据清洗案例实操(复杂解析版)
    • 需求:
      对Web访问日志中的各字段识别切分,去除日志中不合法的记录。根据清洗规则,输出过滤后的数据
    • 代码实现:
      ①定义一个bean,用来记录日志数据中的各数据字段
public class LogBean {
    private String remote_addr;// 记录客户端的ip地址
    private String remote_user;// 记录客户端用户名称,忽略属性"-"
    private String time_local;// 记录访问时间与时区
    private String request;// 记录请求的url与http协议
    private String status;// 记录请求状态;成功是200
    private String body_bytes_sent;// 记录发送给客户端文件主体内容大小
    private String http_referer;// 用来记录从那个页面链接访问过来的
    private String http_user_agent;// 记录客户浏览器的相关信息

    private boolean valid = true;// 判断数据是否合法

    public String getRemote_addr() {
        return remote_addr;
    }

    public void setRemote_addr(String remote_addr) {
        this.remote_addr = remote_addr;
    }

    public String getRemote_user() {
        return remote_user;
    }

    public void setRemote_user(String remote_user) {
        this.remote_user = remote_user;
    }

    public String getTime_local() {
        return time_local;
    }

    public void setTime_local(String time_local) {
        this.time_local = time_local;
    }

    public String getRequest() {
        return request;
    }

    public void setRequest(String request) {
        this.request = request;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getBody_bytes_sent() {
        return body_bytes_sent;
    }

    public void setBody_bytes_sent(String body_bytes_sent) {
        this.body_bytes_sent = body_bytes_sent;
    }

    public String getHttp_referer() {
        return http_referer;
    }

    public void setHttp_referer(String http_referer) {
        this.http_referer = http_referer;
    }

    public String getHttp_user_agent() {
        return http_user_agent;
    }

    public void setHttp_user_agent(String http_user_agent) {
        this.http_user_agent = http_user_agent;
    }

    public boolean isValid() {
        return valid;
    }

    public void setValid(boolean valid) {
        this.valid = valid;
    }

    @Override
    public String toString() {

        StringBuilder sb = new StringBuilder();
        sb.append(this.valid);
        sb.append("\001").append(this.remote_addr);
        sb.append("\001").append(this.remote_user);
        sb.append("\001").append(this.time_local);
        sb.append("\001").append(this.request);
        sb.append("\001").append(this.status);
        sb.append("\001").append(this.body_bytes_sent);
        sb.append("\001").append(this.http_referer);
        sb.append("\001").append(this.http_user_agent);

        return sb.toString();
    }
}

②编写LogMapper类

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 获取1行
        String line = value.toString();

        // 2 解析日志是否合法
        LogBean bean = parseLog(line);

        if (!bean.isValid()) {
            return;
        }

        k.set(bean.toString());

        // 3 输出
        context.write(k, NullWritable.get());
    }

    // 解析日志
    private LogBean parseLog(String line) {

        LogBean logBean = new LogBean();

        // 1 截取
        String[] fields = line.split(" ");

        if (fields.length > 11) {

            // 2封装数据
            logBean.setRemote_addr(fields[0]);
            logBean.setRemote_user(fields[1]);
            logBean.setTime_local(fields[3].substring(1));
            logBean.setRequest(fields[6]);
            logBean.setStatus(fields[8]);
            logBean.setBody_bytes_sent(fields[9]);
            logBean.setHttp_referer(fields[10]);

            if (fields.length > 12) {
                logBean.setHttp_user_agent(fields[11] + " "+ fields[12]);
            }else {
                logBean.setHttp_user_agent(fields[11]);
            }

            // 大于400,HTTP错误
            if (Integer.parseInt(logBean.getStatus()) >= 400) {
                logBean.setValid(false);
            }
        }else {
            logBean.setValid(false);
        }

        return logBean;
    }
}

③编写LogDriver类

public class LogDriver {
    public static void main(String[] args) throws Exception {

// 1 获取job信息
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        // 2 加载jar包
        job.setJarByClass(LogDriver.class);

        // 3 关联map
        job.setMapperClass(LogMapper.class);

        // 4 设置最终输出类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);

        // 5 设置输入和输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        // 6 提交
        job.waitForCompletion(true);
    }
}
<think>嗯,用户想了解MapReduce进行数据清洗和处理的教程。首先,我需要确认用户对MapReduce的基础是否熟悉。可能他们已经有Hadoop的基础,但不确定,所以最好先简要介绍MapReduce的基本概念,然后再进入数据清洗和处理的具体步骤。 数据清洗通常包括去除重复数据、处理缺失值、纠正格式错误等。在MapReduce中,这些操作可以通过Mapper和Reducer来实现。需要举一些具体的例子,比如过滤无效数据、格式标准化、去重等。可能用户需要实际的代码示例,比如用Java写的MapReduce程序,但也要考虑到用户可能对编程不太熟悉,所以需要解释每个步骤的作用。 另外,数据处理可能涉及更复杂的操作,比如聚合、统计或连接多个数据集。这时候可能需要多个MapReduce作业或者链式处理。需要提到Combiner的作用,以及如何优化性能,比如减少数据传输量。 用户可能关心实际应用场景,比如日志分析、ETL流程等,可以举一个电商数据清洗的例子,这样更直观。还要注意提醒常见问题,比如数据倾斜、性能优化,以及错误处理机制,比如使用计数器记录异常数据。 最后,确保回答结构清晰,分步骤讲解,先理论后实例,再总结注意事项。同时检查LaTeX格式是否正确,比如数学公式用$...$,独立公式用$$...$$,避免语法错误。还要用中文,保持口语化,但不要用Markdown格式,段落之间用换行分隔。</think>MapReduce数据清洗与处理教程(分步解析) 一、MapReduce基础概念 $$MapReduce = Map(映射) + Shuffle(排序) + Reduce(归约)$$ 适用于海量数据的分布式处理,核心思想是"分而治之"。每个Map处理数据块,Reduce汇总结果。 二、数据清洗典型场景 1. 无效值过滤:$filter\ (age < 0\ ||\ age > 150)$ 2. 格式标准化:$date\ format\ (YYYYMMDD \rightarrow YYYY-MM-DD)$ 3. 重复数据去重:$distinct\ (user\_id)$ 4. 缺失值处理:$fill\_null\ (default=unknown)$ 三、完整处理流程示例(电商日志清洗) ```java // Mapper处理单条日志 public void map(LongWritable key, Text value, Context context) { try { String[] fields = value.toString().split("\t"); if(fields.length != 8) { // 验证字段完整性 context.getCounter("Error", "FieldCount").increment(1); return; } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); Date logDate = sdf.parse(fields[3]); // 日期格式验证 // 构造标准化输出 output.set(fields[0] + "|" + fields[1] + "|" + sdf.format(logDate)); context.write(NullWritable.get(), output); } catch (Exception e) { context.getCounter("Error", "ParseError").increment(1); } } // Reducer去重处理 public void reduce(NullWritable key, Iterable<Text> values, Context context) { HashSet<String> uniqueSet = new HashSet<>(); for (Text val : values) { if(!uniqueSet.contains(val.toString())) { uniqueSet.add(val.toString()); context.write(NullWritable.get(), val); } } } ``` 四、进阶处理技巧 1. 多阶段清洗:通过多个MapReduce作业链式处理 $$Job1(清洗) \rightarrow Job2(转换) \rightarrow Job3(聚合)$$ 2. 使用Combiner预聚合: $$Combiner\ 执行本地reduce操作,减少网络传输量$$ 3. 自定义Writable类型:实现复杂数据结构序列化 五、性能优化建议 1. 合理设置Map数量:$blocksize = 128MB$时最佳 2. 压缩中间结果:推荐使用Snappy压缩格式 3. 数据倾斜处理: - 添加随机前缀分散热点数据 - 使用二次排序 六、调试与监控 1. 通过Counters跟踪清洗结果: $$ValidRecords = TotalRecords - ErrorRecords$$ 2. 使用Hadoop Web UI监控作业进度 3. 日志分析建议: - 记录原始异常数据路径 - 定期统计错误类型分布 七、典型应用场景 1. 日志文件清洗(Web访问日志、IoT传感器数据) 2. 结构化数据ETL(从CSV到Parquet格式转换) 3. 非结构化文本处理(自然语言预处理) 注意事项: 1. 数据验证应前置:尽量在Map阶段完成校验 2. 内存管理:Reducer避免加载全部数据集 3. 版本控制:维护清洗规则版本,便于回溯 扩展学习: 推荐使用Apache Hive进行SQL化清洗(底层仍基于MapReduce),或迁移到Spark引擎获取更佳性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值