Hadoop 中的大数据技术:MapReduce(1)

Hadoop 中的大数据技术:MapReduce(1)

第1章 MapReduce 概述

1.1 MapReduce 定义

MapReduce 是一个用于分布式计算的编程框架,它是 Hadoop 核心组件之一,主要用于开发分布式数据分析应用。

MapReduce 的主要功能是将用户编写的业务逻辑代码与默认组件相结合,形成一个完整的分布式计算程序,并能够在 Hadoop 集群上并发执行。

1.2 MapReduce 优缺点

1.2.1 优点
  1. 易于编程:用户只需要实现几个简单的接口就能完成分布式程序的开发,这使得 MapReduce 成为了非常流行的编程模型。
  2. 良好的扩展性:可以通过简单地增加硬件资源来提升计算能力。
  3. 高容错性:设计时考虑到了运行在廉价硬件上的情况,因此具备自动故障恢复的能力。
  4. 适合大规模数据处理:支持PB级别的数据量处理,可利用上千台服务器集群进行并行计算。
1.2.2 缺点
  1. 不擅长实时计算:响应时间通常较长,不适合需要快速返回结果的应用场景。
  2. 不擅长流式计算:输入数据集必须是静态的,不适合持续更新的数据流处理。
  3. 不擅长 DAG 计算:多个任务之间存在依赖时,中间结果频繁写入磁盘会导致性能下降。

1.3 MapReduce 核心思想

  1. 程序分为两个阶段:Map 和 Reduce 阶段。
  2. Map 阶段:多个 MapTask 实例并行运行,处理数据。
  3. Reduce 阶段:多个 ReduceTask 实例处理来自所有 MapTask 的输出。
  4. 单个 MapReduce 作业:只包含一个 Map 阶段和一个 Reduce 阶段;对于复杂的业务逻辑,可能需要多个 MapReduce 作业串行执行。

1.4 MapReduce 进程

  1. MrAppMaster:负责整个 MapReduce 程序的任务调度和状态协调。
  2. MapTask:负责 Map 阶段的数据处理。
  3. ReduceTask:负责 Reduce 阶段的数据处理。

1.5 WordCount 示例详解

1.5.1 环境准备
  1. 创建 Maven 工程:MapReduceDemo
  2. 添加依赖:在 pom.xml 文件中加入 Hadoop 和其他必要依赖。
  3. 配置日志:在 src/main/resources/log4j.properties 中设置日志级别和输出格式。
  4. 创建 包com.lzl.mapreduce.wordcount
1.5.2 编写程序
Mapper 类
package com.lzl.mapreduce.wordcount;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    
    private final static IntWritable ONE = new IntWritable(1);
    private Text word = new Text();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] words = line.split("\\s+");
        for (String w : words) {
            word.set(w);
            context.write(word, ONE);
        }
    }
}
Reducer 类
package com.lzl.mapreduce.wordcount;

import .io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

    private IntWritable result = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int sum = 0;
        for (IntWritable val : values) {
            sum += val.get();
        }
        result.set(sum);
        context.write(key, result);
    }
}
Driver 类
package com.lzl.mapreduce.wordcount;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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 WordCountDriver {

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "word count");
        job.setJarByClass(WordCountDriver.class);
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}
1.5.3 测试
本地测试
  1. 配置 Hadoop 环境:确保 Hadoop 环境变量已正确设置。
  2. 运行程序:在 IDE 中运行 WordCountDriver 类。
集群测试
  1. 打包:使用 Maven 打包工具将程序打包成包含依赖的 JAR 文件。

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    
  2. 上传 JAR 文件:将 JAR 文件上传至集群指定目录。

  3. 启动集群:使用脚本启动 Hadoop 集群。

  4. 执行程序:使用 hadoop jar 命令提交作业。

    [lzl@hadoop12 hadoop-3.1.3]$ sbin/start-dfs.sh
    [lzl@hadoop13 hadoop-3.1.3]$ sbin/start-yarn.sh
    [lzl@hadoop12 hadoop-3.1.3]$ hadoop jar  wc.jar com.lzl.mapreduce.wordcount.WordCountDriver /user/lzl/input /user/lzl/output
    

第2章 Hadoop序列化

2.1 序列化概述

2.1.1 什么是序列化
  • 定义:序列化是指将内存中的对象转换为字节序列的过程,以便于存储或在网络中传输。
  • 反序列化:将接收到的字节序列还原为内存中的对象。
2.1.2 为什么需要序列化
  • 存储和传输:序列化允许将“活的”对象存储到磁盘或发送到远程计算机。
  • 提高效率:序列化可以减少存储空间和网络传输的开销。
2.1.3 为什么不使用原生序列化
  • 效率问题:的序列化机制较为臃肿,包含了很多不必要的元数据,不适合高性能的网络传输。
  • Hadoop自定义序列化:Hadoop采用了更轻量级的序列化机制(Writable接口),以提高效率和兼容性。
2.1.4 Hadoop序列化的特点
  • 紧凑性:高效利用存储空间。
  • 高速度:低开销的读写操作。
  • 互操作性:支持多种语言间的交互。

2.2 自定义bean对象实现序列化接口(Writable)

2.2.1 实现步骤
  1. 实现Writable接口
  2. 提供无参构造函数:用于反序列化时的反射调用。
  3. 重写序列化方法write() 方法用于序列化。
  4. 重写反序列化方法readFields() 方法用于反序列化。
  5. 保持序列化和反序列化顺序一致
  6. 重写toString()方法:便于输出结果。
  7. 实现Comparable接口:如果bean对象需要作为key使用,则需要能够排序。
2.2.2 示例代码
流量统计Bean对象
package com.lzl.mapreduce.writable;

import org.apache.hadoop.io.Writable;
import .io.DataInput;
import .io.DataOutput;
import .io.IOException;

public class FlowBean implements Writable {

    private long upFlow; // 上行流量
    private long downFlow; // 下行流量
    private long sumFlow; // 总流量

    public FlowBean() {}

    public long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(long upFlow) {
        this.upFlow = upFlow;
    }

    public long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(long downFlow) {
        this.downFlow = downFlow;
    }

    public long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    public void setSumFlow() {
        this.sumFlow = this.upFlow + this.downFlow;
    }

    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeLong(upFlow);
        dataOutput.writeLong(downFlow);
        dataOutput.writeLong(sumFlow);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        this.upFlow = dataInput.readLong();
        this.downFlow = dataInput.readLong();
        this.sumFlow = dataInput.readLong();
    }

    @Override
    public String toString() {
        return upFlow + "\t" + downFlow + "\t" + sumFlow;
    }
}
Mapper类
package com.lzl.mapreduce.writable;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import .io.IOException;

public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean> {
    private Text outK = new Text();
    private FlowBean outV = new FlowBean();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] fields = line.split("\t");
        String phone = fields[1];
        String up = fields[fields.length - 3];
        String down = fields[fields.length - 2];

        outK.set(phone);
        outV.setUpFlow(Long.parseLong(up));
        outV.setDownFlow(Long.parseLong(down));
        outV.setSumFlow();

        context.write(outK, outV);
    }
}
Reducer类
package com.lzl.mapreduce.writable;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import .io.IOException;

public class FlowReducer extends Reducer<Text, FlowBean, Text, FlowBean> {
    private FlowBean outV = new FlowBean();

    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
        long totalUp = 0;
        long totalDown = 0;

        for (FlowBean flowBean : values) {
            totalUp += flowBean.getUpFlow();
            totalDown += flowBean.getDownFlow();
        }

        outV.setUpFlow(totalUp);
        outV.setDownFlow(totalDown);
        outV.setSumFlow();

        context.write(key, outV);
    }
}
Driver驱动类
package com.lzl.mapreduce.writable;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;
import .io.IOException;

public class FlowDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        job.setJarByClass(FlowDriver.class);

        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        boolean success = job.waitForCompletion(true);
        System.exit(success ? 0 : 1);
    }
}

感谢您阅读完这篇文章,如果您觉得有所收获,别忘了关注我的公众号[大数据深度洞察],更多精彩内容等您来探索!”
“希望本文能对您有所帮助,如果您还想获取更多类似的优质内容,欢迎关注我的公众号[大数据深度洞察],让我们一起在技术的海洋中遨游!”
“如果您喜欢这篇文章的内容,期待在公众号[大数据深度洞察]中与您有更多的交流,那里有更多深度的技术文章和实用的技巧等着您!
在这里插入图片描述

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值