MapReduce 思想

MapReduce 是 Google 提出的一个软件架构,用于大规模数据集的并行运算。概率“Map(映射)”和“Reduce(归约)”以及它们的思想都是从函数式编程语言借鉴的,还有从矢量编程语言借来的特性。

当前的软件实现是指定一个“Map”函数,用来把一组键值对映射成一组新的键值对,指定并发的“Reduce”函数,用来保证所有映射的键值对中的每一个都共享相同的键组。

Hadoop MapReduce 的任务过程分为两个阶段:

  • Map 阶段:把大任务分解为若干个小任务来并行处理。这些任务可以并行计算,彼此之间没有依赖关系。
  • Reduce 阶段:对 map 阶段的结果进行全局汇总。

Hadoop 序列化

为什么要序列化?

序列化是我们通过网络通信传输数据时或者把对象持久化到文件,需要把对象序列化成二进制的结构。

观察源码时发现自定义 Mapper 类与自定义 Reducer 类都有泛型类约束,比如自定义 Mapper 有四个泛型参数,但是都不是 Java 基本类型。

为什么 Hadoop 要选择建立自己的序列化格式而不使用 java 自带 serializable?

  • 序列化在分布式程序中非常重要,在 Hadoop 中,集群中多个节点的进程间的通信是通过 RPC(远程过程调用:RemoteProcedureCall)实现;RPC 将消息序列化成二进制流发送到远程节点,远程节点再将接收到的二进制数据反序列化为原始的消息,因此 RPC 往往追求如下特点:

    • 数据更紧凑,能充分利用网络带宽资源
    • 快速:序列化和反序列化的性能开销更低
  • Hadoop 使用的是自己的序列化格式 Writable,它比 java 的序列化 serialization 更紧凑速度更快。一个对象使用 Serializable 序列化后,会携带很多额外信息比如校验信息,Header,继承体系等

Java 基本类型与 Hadoop 常用序列化类型

Java 基本类型 Hadoop Writable 类型
boolean BooleanWritable
byte ByteWritable
int IntWritable
float FloatWritable
long LongWritable
double DoubleWritable
String Text
map MapWritable
array ArrayWritable

基本的序列化类型往往不能满足需求,比如我们常常需要传递一些自定义的 bean 对象。在 Hadoop 中为了实现自定义对象序列化需要实现 Writable 接口。

  1. 实现 Writable 接口
  2. 有无参构造函数
  3. 重写序列化 write 方法和反序列化 readFields 方法。(注意序列化和反序列化的字段顺序必须完全一致)
  4. 如果自定义 Bean 对象需要放在 Mapper 输出 KV 中的 K 里面,那么该对象还需要实现 Comparable 接口,因为 MapReduce 框架中的 Shuffle 过程要求 key 必须能排序

案例实战

需求:下面有一个水果摊老板的一个售卖记录,这三列分别是:水果名称、水果重量、还有总价。我们需要统计每个水果的总重量和重价。

苹果 3 12
李子 4 8
苹果 2 8
桃子 4 20
香蕉 2 4
火龙果 1 4
  1. 配置 Hadoop 环境变量
  2. 导入 maven 依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop-version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop-version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop-version}</version>
</dependency>
  1. 编写保存售卖记录的实体类
@Setter
@Getter
public class FruitsRecord implements Writable { private int weight; private double totalPrice; @Override
public void write(DataOutput out) throws IOException {
out.writeInt(weight);
out.writeDouble(totalPrice);
} @Override
public void readFields(DataInput in) throws IOException {
this.weight = in.readInt();
this.totalPrice = in.readDouble();
} @Override
public String toString() {
return "FruitsRecord{" +
"weight=" + weight +
", totalPrice=" + totalPrice +
'}';
}
}
  1. 编写 Mapper 类
import com.mmc.hadoop.bean.FruitsRecord;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; public class FruitsMapper extends Mapper<LongWritable,Text,Text, FruitsRecord> { @Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, FruitsRecord>.Context context) throws IOException, InterruptedException {
//获取一行的数据
String line = value.toString(); String[] fields = line.split(" "); Text outKey= new Text(fields[0]);
FruitsRecord fruitsRecord=new FruitsRecord();
fruitsRecord.setWeight(Integer.parseInt(fields[1]));
fruitsRecord.setTotalPrice(Double.parseDouble(fields[2])); context.write(outKey,fruitsRecord);
}
}
  1. 编写 Reduce 类
import com.mmc.hadoop.bean.FruitsRecord;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; public class FruitsReducer extends Reducer<Text, FruitsRecord,Text,FruitsRecord> { @Override
protected void reduce(Text key, Iterable<FruitsRecord> values, Reducer<Text, FruitsRecord, Text, FruitsRecord>.Context context) throws IOException, InterruptedException {
int totalWeight = 0;
double totalPrice =0; for (FruitsRecord fruitsRecord : values){
totalWeight += fruitsRecord.getWeight();
totalPrice+= fruitsRecord.getTotalPrice();
} FruitsRecord fruitsRecord = new FruitsRecord();
fruitsRecord.setWeight(totalWeight);
fruitsRecord.setTotalPrice(totalPrice); context.write(key, fruitsRecord); }
}
  1. 编写 Driver 类
import com.mmc.hadoop.bean.FruitsRecord;
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 java.io.IOException; public class FruitsDriver { public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
// System.setProperty("java.library.path","d://");
Configuration conf = new Configuration();
Job job=Job.getInstance(conf,"FruitsDriver"); //指定本程序的jar包所在的路径
job.setJarByClass(FruitsDriver.class); //指定本业务job要使用的mapper/Reducer业务类
job.setMapperClass(FruitsMapper.class);
job.setReducerClass(FruitsReducer.class); //指定mapper输出数据的kv类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(FruitsRecord.class); //指定reduce输出数据的kv类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FruitsRecord.class); //指定job的输入文件目录和输出目录
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1])); boolean result = job.waitForCompletion(true);
System.exit( result ? 0: 1); }
}

总结:

Mapper 里面,Mapper 类的四个泛型分别为入参的 KV 和出参的 KV。Reduce 里面的也有4个泛型,分别为入参的KV和出参的KV。Reduce入参的 KV 与 Mapper 里面出参的 KV 类型是对应的。只不过 Reduce 的入参的 Value 类型是集合类型的。

时序图如下:

运行任务

本地模式

直接在 IDEA 中运行驱动类即可。因为程序里输入文件路径和输出文件路径是取的 main 函数里的 args。所以运行的时候需要指定参数。

遇到的问题

问题 1:

问题 2:创建目录错误

org.apache.hadoop.io.nativeio.NativeIO$Windows.createDirectoryWithMode0(Ljava/lang/String

解决方案:

两个问题都是 windows 的 hadoop/bin 目录下缺少文件导致的。文件下载路径:https://github.com/cdarlint/winutils

  1. 找到对应版本的 hadoop.dll 和 winutils.exe 下载下来放到 hadoop/bin 目录下。
  2. C: windows\System32 放入 hadoop.dll 文件
  3. 重启电脑

输出目录:

打开结果文件 part-r-00000:

李子	FruitsRecord{weight=4, totalPrice=8.0}
桃子 FruitsRecord{weight=4, totalPrice=20.0}
火龙果 FruitsRecord{weight=1, totalPrice=4.0}
苹果 FruitsRecord{weight=5, totalPrice=20.0}
香蕉 FruitsRecord{weight=2, totalPrice=4.0}

Yarn 集群模式

  1. 把程序打包成 jar 包,上传到 linux
  2. 将测试的 txt 上传到 HDFS 上面
  3. 启动 Hadoop 集群
  4. 使用 Hadoop 命令提交任务运行
hadoop jar wc.jar com.mmc.hadoop.FruitsDriver
/user/input /user/output

MapReduce入门实战的更多相关文章

  1. Spark入门实战系列--10.分布式内存文件系统Tachyon介绍及安装部署

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Tachyon介绍 1.1 Tachyon简介 随着实时计算的需求日益增多,分布式内存计算 ...

  2. Spark入门实战系列--1.Spark及其生态圈简介

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .简介 1.1 Spark简介 年6月进入Apache成为孵化项目,8个月后成为Apache ...

  3. Spark入门实战系列--2.Spark编译与部署(中)--Hadoop编译安装

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .编译Hadooop 1.1 搭建环境 1.1.1 安装并设置maven 1. 下载mave ...

  4. Spark入门实战系列--2.Spark编译与部署(下)--Spark编译安装

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .编译Spark .时间不一样,SBT是白天编译,Maven是深夜进行的,获取依赖包速度不同 ...

  5. Spark入门实战系列--3.Spark编程模型(上)--编程模型及SparkShell实战

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Spark编程模型 1.1 术语定义 l应用程序(Application): 基于Spar ...

  6. Spark入门实战系列--4.Spark运行架构

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 1. Spark运行架构 1.1 术语定义 lApplication:Spark Appli ...

  7. Spark入门实战系列--5.Hive(上)--Hive介绍及部署

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Hive介绍 1.1 Hive介绍 月开源的一个数据仓库框架,提供了类似于SQL语法的HQ ...

  8. Spark入门实战系列--6.SparkSQL(上)--SparkSQL简介

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .SparkSQL的发展历程 1.1 Hive and Shark SparkSQL的前身是 ...

  9. Spark入门实战系列--8.Spark MLlib(上)--机器学习及SparkMLlib简介

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .机器学习概念 1.1 机器学习的定义 在维基百科上对机器学习提出以下几种定义: l“机器学 ...

随机推荐

  1. R-CNN学习笔记

    R-CNN学习笔记 step1:总览 步骤: 输入图片 先挑选大约2000个感兴趣区域(ROI)使用select search方法:[在输入的图像中寻找blobby regions(可能相同纹理,颜色 ...

  2. 史上最全Spring Cloud Alibaba--Nacos教程(涵盖负载均衡、配置管理、多环境切换、配置共享/刷新、灰度、集群)

    能够实现Nacos安装 基于Nacos能实现应用负载均衡 能基于Nacos实现配置管理 配置管理 负载均衡 多环境切换 配置共享 配置刷新 灰度发布 掌握Nacos集群部署 1 Nacos安装 Nac ...

  3. DYOJ 【20220317模拟赛】瞬间移动 题解

    瞬间移动 题意 三维空间中从 \((0,0,0)\) 开始,每次移动 1,问刚好走 \(N\) 次能到 \((X,Y,Z)\) 的方案数 \(N\le10^7\),答案模 \(998244353\) ...

  4. Acwing787.归并排序

    Acwing787.归并排序 归并模板 归并排序,合二为一 题目链接:Acwing787.归并排序 #include<iostream> using namespace std; cons ...

  5. 攻防世界pwn题:实时数据检测

    0x00:查看文件 一个32位的文件,canary.NX.PIE保护机制均关闭. 0x01:用IDA进行静态分析 程序很简单,输入一串字符(个数限制:512),然后再输出.最后根据key变量进行条件语 ...

  6. linux-基于tensorflow2.x的手写数字识别-基于MNIST数据集

    数据集 数据集下载MNIST 首先读取数据集, 并打印相关信息 包括 图像的数量, 形状 像素的最大, 最小值 以及看一下第一张图片 path = 'MNIST/mnist.npz' with np. ...

  7. 设计模式-策略模式前端应用校验vue写法

    1.定义:定义一系列算法,把它们一个个封装起来,并且它们可以相互替换 2.实际应用:减少if else的使用,在有多种算法相似的情况下,使用 if-else 所带来的复杂和难以维护,提高维护和可读性, ...

  8. Python: list列表的11个内置方法

    先来逼逼两句: 在实际开发中,经常需要将一组(不只一个)数据存储起来,以便后边的代码使用.在VBA中有使用数组,可以把多个数据存储到一起,通过数组下标可以访问数组中的每个元素.Python 中没有数组 ...

  9. 013(oulipo)

    题目:http://ybt.ssoier.cn:8088/problem_show.php?pid=1455 题目描述:在母串里找子串出现的次数 题目思路:与字符串的搜索有关那就立刻找到哈希 从s[1 ...

  10. Future源码一观-JUC系列

    背景介绍 在程序中,主线程启动一个子线程进行异步计算,主线程是不阻塞继续执行的,这点看起来是非常自然的,都已经选择启动子线程去异步执行了,主线程如果是阻塞的话,那还不如主线程自己去执行不就好了.那会不 ...