一、MapReduce介绍
MapReduce是一个分布式计算框架
,可以部署在Hadoop、Spark等大数据平台上,实现海量数据的并行计算。它采用“分而治之
”的思想,将一个计算任务交给集群中的多台机器共同完成,之后再汇总成最终结果。
一般来说读取一个TB,PB级的文件,普通计算机的速度是比较慢的,而要想提高速度就要提高计算机的硬件配置,这对于普通用户来说是很难做到的,也提高了这一领域的门槛。而采用廉价的机器组成分布式系统,只要集群的机器数量足够多,那么计算的速度就会足够快。
二、MapReduce特点
优点
(1)易于编程
完全独立完成一个MapReduce程序是一个很困难的事情,这需要很强的编程能力。好在MapReduce给我们提供了大量的方便开发的接口,我们只需要继承一些接口,实现一些特定的函数就能完成一个MapReduce程序。
(2)高拓展性
这是一个分布式计算框架,我们可以简单粗暴的,通过增加机器来提高计算性能。
(3)高容错性
由于MapReduce集群采用的大多是廉价的机器,宕机,BUG等都是家常便饭。但MapReduce框架提供多种有效的错误检测和恢复机制。如果一个结点出现了问题,其他结点会接替这个结点的工作,等结点恢复正常后,又可以继续工作,这些都由Hadoop内部完成。
(4)高吞吐量
MapReduce可以对PB级以上也就是1024TB的数据进行离线计算。
缺点
(1)难以实时计算
MapReduce处理的是磁盘上的数据。
(2)不能流式计算
MapReduce处理的是磁盘上的静态数据,而流式计算的输入数据的动态的。
(3)难以用于DAG计算
DAG(有向无环图)多个任务间存在依赖关系,后一个应用的输入可能是前一个应用的输出。而MapReduce的输出结果都会写在磁盘上,这会造成大量的磁盘IO,降低集群的性能。
三、MapReduce编程
以词频统计程序为例
pom依赖
去https://mvnrepository.com/寻找以下几个依赖
hadoop-common、hadoop-hdfs、hadoop-mapreduce-client-core、junit
插件:maven-compiler-plugin、maven-shade-plugin
下面是我的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.pineapple</groupId>
<artifactId>MapReduceTest</artifactId>
<!-- <packaging>pom</packaging>-->
<version>1.0-SNAPSHOT</version>
<properties>
<hadoop.version>2.6.0</hadoop.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-client-core -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<!-- <scope>test</scope>-->
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-shade-plugin -->
<!-- <dependency>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-shade-plugin</artifactId>-->
<!-- <version>2.4.3</version>-->
<!-- </dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
要做统计的文件内容为:
Hello,Hadoop,BigData
Hello,Hadoop,MapReduce
Hello,Hadoop,HDFS
BigData,Perfect
MapReduce由Map和Reduce两个阶段组成。
Map
在此之前还有一个读取文件的操作,这个只要在主类中指定一下就好,不需要写。
读取后文件的内容为:
//K1 V1
0 Hello,Hadoop,BigData
21 Hello,Hadoop,MapReduce
44 Hello,Hadoop,HDFS
62 BigData,Perfect
Map后的文件内容为:
//K2 V2
Hello 1
Hadoop 1
BigData 1
Hello 1
Hadoop 1
MapReduce 1
Hello 1
Hadoop 1
HDFS 1
BigData 1
Perfect 1
Map表示“映射”,将文件拆分成多个块,然后发给集群上的机器统一计算要继承Mapper类并重写map()函数
,将K1,V1转换成下面的K2,V2
package cn.pineapple.day1;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* 四个泛型的解释:
* <p>
* KEYIN:K1的类型
* <p>
* VALUEIN:V1的类型
* <p>
* KEYOUT:K2的类型
* <p>
* VALUEOUT:V2的类型
*/
public class WordCountMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
/**
* 将K1,V1转换成K2,V2
*
* @param key: 行偏移量
* @param value: 一行文本内容
* @param context: 上下文对象
* @throws IOException:
* @throws InterruptedException:
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
Text text = new Text();
LongWritable longWritable = new LongWritable();
//1:对一行的文本数据进行拆分
String[] split = value.toString().split(",");
//2:遍历数组,组装K2和V2
for (String word : split) {
//3:将K2和V2写入上下文中
text.set(word);
longWritable.set(1);
context.write(text, longWritable);
}
}
}
map()函数内的代码,只需要针对文件一行考虑。所以首先要提取每个单词,用value.toString()将value也就是这一行的内容转换成String类型,再用split()方法进行拆分。K2的值默认为1。在写入的时候,还要将K2和V2转换成Text类型和LongWritable类型。
Reduce
在Map和Reduce中间还有一个shuffle,目前用不到这个shuffle,可以采取默认的方式,它会把K2,V2转换成:
//K2 V2
Hello <1,1,1>
Hadoop <1,1,1>
BigData <1,1>
HDFS <1>
MapReduce <1>
Perfect <1>
接着Reduce的最终结果是:
//K3 V3
BigData 2
HDFS 1
Hadoop 3
Hello 3
MapReduce 1
Perfect 1
Reduce表示“归约”,将所有结果都统一起来,继承Reducer类并重写reduce()函数
,将K2和V2转换为K3和V3,也就是最终结果。
package cn.pineapple.day1;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* 四个泛型的解释:
* <p>
* KEYIN:K2类型
* <p>
* VALUEIN:V2类型
* <p>
* KEYOUT:K3类型
* <p>
* VALUEOUT:V3类型
*/
public class WordCountReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
/**
* 将K2和V2转换为K3和V3,将K3和V3写入上下文中
*
* @param key: 新K2
* @param values: 新V2
* @param context: 上下文对象
* @throws IOException:
* @throws InterruptedException:
*/
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
long count = 0;
//1:遍历集合,将集合中的数字相加,得到V3
for (LongWritable value : values) {
count += value.get();
}
//2:将K3V3写入上下文中
context.write(key, new LongWritable(count));
}
}
为了实现这个最终结果,K3和K2是一样的,我们只需要遍历这个集合,然后相加就能得到V3。value.get()可以将LongWritable类型转换成long类型,最后在写入的时候再进行一次转换。
Main方法
光有这两个类是不行的,我们还要写一个Main方法,详细的列一下任务流程,指定一下任务配置等。要继承Configured类并实现Tool接口。
package cn.pineapple.day1;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
public class JobMain extends Configured implements Tool {
/**
* 指定一个job任务
*
* @param strings:
* @return :
* @throws Exception:
*/
public int run(String[] strings) throws Exception {
//1:创建一个job任务对象
Job wordCount = Job.getInstance(super.getConf(), "WordCount");
//如果打包运行出错,则需要加改配置
wordCount.setJarByClass(JobMain.class);
//2:配置job任务对象(八个步骤)
//第一步:指定文件的读取方式和
wordCount.setInputFormatClass(TextInputFormat.class);
// 读取路径
TextInputFormat.addInputPath(wordCount, new Path("hdfs://nsv:8020/input/wordcount"));
//第二步:指定Map阶段的处理方式和数据类型
wordCount.setMapperClass(WordCountMapper.class);
// 设置K2的类型
wordCount.setMapOutputKeyClass(Text.class);
// 设置V2的类型
wordCount.setMapOutputValueClass(LongWritable.class);
//第三、四、五、六shuffle阶段采用默认的方式
//第七步:指定Reduce阶段的处理方式和数据类型
wordCount.setReducerClass(WordCountReducer.class);
// 设置K3的类型
wordCount.setOutputKeyClass(Text.class);
// 设置V3的类型
wordCount.setOutputValueClass(LongWritable.class);
//第八步:指定输出类型
wordCount.setOutputFormatClass(TextOutputFormat.class);
// 输出路径
TextOutputFormat.setOutputPath(wordCount, new Path("hdfs://nsv:8020/output/wordcount"));
//等待任务结束
boolean bl = wordCount.waitForCompletion(true);
return bl ? 0 : 1;
}
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
//启动job任务
int run = ToolRunner.run(configuration, new JobMain(), args);
System.exit(run);
}
}
run()方法里就是本次任务的详细流程和配置,然后main()方法里要调用上面写的run方法。它接受一个int类型作为退出代码,0或1。
四、打包运行
双击package打包,出现BUILD SUCCESS表示打包成功
出现了两个jar包,origin包是因为用了maven-shade-plugin打包插件,不会包含依赖jar包,所有体积较小,可以选择这个jar包运行。
将要统计的文件放到HDFS上hdfs dfs -mkdir -p /input/wordcount
hdfs dfs -put wordcount.txt /input/wordcount
将jar包上传到集群上后,跑一下喽hadoop jar original-MapReduceTest-1.0-SNAPSHOT.jar cn.pineapple.day1.JobMain
,cn.pineapple.day1.JobMain是主类的全路径
completed successfully看来是成功了,去Web上看一下实际结果
点击Download可以查看下载结果