目录
9、编写一个FileUtils .java 删除方法,当output文件夹存在时,对其先进行删除
2、在如下处建立 log4j.properties 进行打印日志
(3)若导致导致数据倾斜的key(例如a)分布在很多不同的文件?如何解决?
(1)yarn.nodemanager.resource.memory-mb
(2)yarn.scheduler.minimum-allocation-mb
(3)yarn.scheduler.maximum-allocation-mb
一、环境准备
1、本地搭建hadoop环境
将hadoop相关文件放到目录下如下
配置环境变量
在终端输入hadoop version 如下
2、搭建maven
conf下setting文件使用国内镜像
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
环境变量
在终端下输入mvn -v
二、IDEA编写
1、新建一个Maven 工程
界面如下:
2、修改配置仓库文件位置
3、编辑pom.xml 文件 代码如下
<?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>org.example</groupId>
<artifactId>MapReduce</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<properties>
<hadoop.version>3.1.3</hadoop.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
</project>
然后点击maven 进行下载 如下
4、在data下新建一个wc.txt文件 内容如下
5、 新建MRMapper.java
代码如下:
package MR.wc;
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 MRMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
Text k = new Text();
IntWritable v = new IntWritable(1);
@Override
protected void setup(Context context) throws IOException, InterruptedException {
System.out.println("Mapper.setup----------");
}
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 1 获取一行
String line = value.toString();
// 2 切割
String[] words = line.split(" ");
// 3 输出
for (String word : words) {
k.set(word);
context.write(k, v);
}
}
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
System.out.println("Mapper.cleanup");
}
}
6、新建MRReducer.java
package MR.wc;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class MRReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
int sum;
IntWritable v = new IntWritable();
@Override
protected void setup(Context context) throws IOException, InterruptedException {
System.out.println("Mapper.setup__________");
}
@Override
protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
// 1 累加求和
sum = 0;
for (IntWritable count : values) {
sum += count.get();
}
// 2 输出
v.set(sum);
context.write(key, v);
}
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
System.out.println("Mapper.cleanup__________");
}
}
7、新建MRDriver.java 文件
package MR.wc;
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 MRDriver {
public static void main(String[] args) throws Exception {
String input ="data/wc.txt";
String output ="out_mr_wc";
final Configuration configuration = new Configuration();
//1获取job对象
final Job job = Job.getInstance(configuration);
//2设置class
job.setJarByClass(MRDriver.class);
//3设置Mapper 和 Reducer
job.setMapperClass(MRMapper.class);
job.setReducerClass(MRReducer.class);
//4设置Map端输出KV类型,即MRMapper的三四数据类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//5设置Redu输出的KV类型,即MRReducer的三四数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6设置程序的输入输出路径
FileInputFormat.setInputPaths(job,new Path(input));
FileOutputFormat.setOutputPath(job, new Path(output));
//7提交Job
final boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}
8、执行如下显示
可在 output文件夹下找到 成功part 内容如下
9、编写一个FileUtils .java 删除方法,当output文件夹存在时,对其先进行删除
package MR.wc;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.io.IOException;
public class FileUtils {
public static void deleteTarget(Configuration configuration,String output) throws IOException{
final FileSystem fileSystem = FileSystem.get(configuration);
final Path path = new Path(output);
if (fileSystem.exists(path)){
fileSystem.delete(path,true);
}
}
}
10、在MRDriver.java 中调用9中方法
11、执行成功
三 、一些总结调整
1、对于各任务的运行解释
map 代表 输入行数
reducer 代表最后结果输出的行数
2、在如下处建立 log4j.properties 进行打印日志
内容如下:
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
在MRDriver main函数入口处添加
BasicConfigurator.configure(); //自动快速地使用缺省Log4j环境
运行 打印日志成功
四、一些调优点
1、Combiner(预聚合)
介于map和reduce之间的一个reduce操作,但是它是运行在MapTask上的
作用:减少数据的网络传输,是一个非常重要的调优点
以下是未进行预聚合log
Map input records=3
Map output records=11
Map output bytes=112
Map output materialized bytes=140
Input split bytes=104
Combine input records=0
Combine output records=0
Reduce input groups=6
Reduce shuffle bytes=140
Reduce input records=11
Reduce output records=6
添加combine 如下:
日志如下
Map input records=3
Map output records=11
Map output bytes=112
Map output materialized bytes=82
Input split bytes=104
Combine input records=11
Combine output records=6
Reduce input groups=6
Reduce shuffle bytes=82
Reduce input records=6
Reduce output records=6
2、Hadoop数据倾斜 (面试点)
(1)什么是数据倾斜?表现?
例如一个文件 里面有 a b c 三类单词,但是a有一亿个,b、c都是一个,对其做wc操作
这样可能会造成 Map1 99% map2 100% map3 100%
(2)解决办法
使用combine
有 (a,1亿)
( b,1)
(c,1)
以此 减少数据的网络传输,但是如果导致数据倾斜的key(例如a)分布在很多不同的文件,不同的mapper,这种方法就不太适合
比如 100个mapper 每个里面有1万个a 那么优化的粒度不是很大
(3)若导致导致数据倾斜的key(例如a)分布在很多不同的文件?如何解决?
方法一 局部聚合+全局聚合
第一次map:对于导致数据倾斜的key(a),加上一个随机数前缀
1_a 2_a 3_a 。。。。等
这样本来相同的key也会被分到多个reducer中进行局部聚合
类似于将原本的串行变成并行
第二次map :去掉随机前缀,进行全局聚合
思想:两次MR,第一次将key随机散列到不同的reducer进行处理,达到负载均衡的目的;
第二次再根据去掉的key的随机前缀,按照本来的key进行reduce处理
方法二 增加reducer数,提高并行度
如下
比如 a b c 本来只有三个reduce,现在将reduce改为30个,也相当于处理a的reducer变多了
当设置为 0 时 ,reducer就不输出了 ,直接输出map结果
方法三 实现自定义分区
partitioner : 按照某种规则 (可以自定义)对map输出的数据进行分区操作
默认的是HASHPartitioner
顺序:map -》 partitioner -》reduce
根据数据分布情况,自定义散列函数,将key均匀分布到不同的reducer
如下:
(4)Shuffle 优化
1、map端
1.1 减少输入的文件个数,对小文件进行合并
1.2 预聚合操作(combine)
2、IO端
数据传输进行压缩
3、 reduce端
3.1 设置map reduce共存
在 mapred-site.xml 中设置,mapreduce.job.reduce.slowstart.completedmaps 默认是 0.05
也就是说 map在执行5%时,开始为reduce进行申请资源,开始执行reduce操作
3.2 尽量少使用reduce
reduce会产生大量的网络消耗,但该用还是得用
3.3 增加reducer,提高并行度
4、整体来看如何优化
4.1 合理的设置map数和reduce数
4.2 mr on yarn yarn进行参数调优
4.3 加配置(机器、内存)
(5)一些面试思考题
1、是否可以只要map,不要reduce ?
可以
联系sql where相当于map
2、是否可以只有reduce,没有map?
可以
五、思考题
1、combine不适合做avg,这是为什么?
因为combine类似于reduce,所以他会先求一次平均值,然后将结果再传给reducer,然后reducer又会做一次平均值,可能造成结果不对。
2、hadoop有几种压缩格式,分别的优缺点与适用场景。
压缩格式 | 优点 | 缺点 | 适用场景 |
snappy压缩 | 高速压缩速度和合理的压缩率;支持Hadoop native库。 | 不支持split;压缩率比gzip要低;Hadoop本身不支持,需要安装;linux系统下没有对应的命令。 | 当MapReduce作业的map输出的数据量比较大的时候,作为map到reduce的中间数据的压缩格式;或者作为一个MapReduce作业的输出和另外一个MapReduce作业的输入。 |
lzo压缩 | 压缩/解压速度也比较快,合理的压缩率;支持split,是Hadoop中最流行的压缩格式;支持Hadoop native库;可以在linux系统下安装lzop命令,使用方便。 | 压缩率比gzip要低一些;hadoop本身不支持,需要安装;在应用中对lzo格式的文件需要做一些特殊处理(为了支持split需要建索引,还需要指定inputformat为lzo格式)。 | 一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,lzo优点越明显。 |
gzip压缩 | 压缩率比较高,而且压缩/解压速度也比较快;Hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样;有Hadoop native库;大部分linux系统都自带gzip命令,使用方便。 | 不支持split | 当每个文件压缩之后在130M以内的,都可以考虑用gzip压缩格式。比如每天的日志压缩成一个gzip文件,运行MapReduce程序的时候通过多个gzip文件达到并发。对于处理这些文件的程序(如Hive、流、MapReduce程序)完全和文本处理一样,压缩之后原来的程序不需要做任何修改。 |
bzip2压缩 | 支持split;具有很高的压缩率,比gzip压缩率都高;Hadoop本身支持,但不支持native;在linux系统下自带bzip2命令,使用方便。 | 压缩/解压速度慢;不支持native。 | 适合对速度要求不高,但需要较高的压缩率的场景。可以作为MapReduce作业的输出格式;输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况;对单个很大的文本文件想压缩减少存储空间,同时又需要支持split,而且兼容之前的应用程序(即应用程序不需要修改)的情况。 |
3、YARN参数调优
Memory资源调度
yarn一般允许用户配置每个节点上可用的物理资源,注意,这里是"可用的",不是物理内存多少,就设置多少,因为一个服务器节点上会有若干的内存,一部分给yarn,一部分给hdfs,一部分给hbase;Member相关的配置如下:
(1)yarn.nodemanager.resource.memory-mb
设置该节点上yarn可使用的内存,默认为8G,如果节点内存资源不足8G,要减少这个值,yarn不会智能的去检测内存资源,一般这个设置yarn的可用内存资源
(2)yarn.scheduler.minimum-allocation-mb
单个任务最小申请物理内存量,默认1024MB,根据自己的业务设定
(3)yarn.scheduler.maximum-allocation-mb
单个任务最大申请物理内存量,默认为8291MB
4、将上诉的IDEA例子打包运行在服务器
(1)使用IDEA将项目进行打包
(2)将jar包传入linux
(3)使用命令对jar包进行运行
[peizk@hadoop test]$ hadoop jar MapReduce2.jar peizk.WC /input/input.txt /output3
(4)结果如下
5、map数和reduce数由什么决定?
(1) map的数量
map的数量通常是由hadoop集群的DFS块大小确定的,也就是输入文件的总块数,正常的map数量的并行规模大致是每一个Node是10~100个,对于CPU消耗较小的作业可以设置Map数量为300个左右,但是由于hadoop的每一个任务在初始化时需要一定的时间,因此比较合理的情况是每个map执行的时间至少超过1分钟。具体的数据分片是这样的,InputFormat在默认情况下会根据hadoop集群的DFS块大小进行分片,每一个分片会由一个map任务来进行处理,当然用户还是可以通过参数mapred.min.split.size参数在作业提交客户端进行自定义设置。还有一个重要参数就是mapred.map.tasks,这个参数设置的map数量仅仅是一个提示,只有当InputFormat 决定了map任务的个数比mapred.map.tasks值小时才起作用。同样,Map任务的个数也能通过使用JobConf 的conf.setNumMapTasks(int num)方法来手动地设置。这个方法能够用来增加map任务的个数,但是不能设定任务的个数小于Hadoop系统通过分割输入数据得到的值。当然为了提高集群的并发效率,可以设置一个默认的map数量,当用户的map数量较小或者比本身自动分割的值还小时可以使用一个相对交大的默认值,从而提高整体hadoop集群的效率。
(2)reduece的数量
reduce在运行时往往需要从相关map端复制数据到reduce节点来处理,因此相比于map任务。reduce节点资源是相对比较缺少的,同时相对运行较慢,正确的reduce任务的个数应该是0.95或者1.75 *(节点数 ×mapred.tasktracker.tasks.maximum参数值)。如果任务数是节点个数的0.95倍,那么所有的reduce任务能够在 map任务的输出传输结束后同时开始运行。如果任务数是节点个数的1.75倍,那么高速的节点会在完成他们第一批reduce任务计算之后开始计算第二批 reduce任务,这样的情况更有利于负载均衡。同时需要注意增加reduce的数量虽然会增加系统的资源开销,但是可以改善负载匀衡,降低任务失败带来的负面影响。同样,Reduce任务也能够与 map任务一样,通过设定JobConf 的conf.setNumReduceTasks(int num)方法来增加任务个数。