day38-hadoop-s-hdfs
day38-hadoop-s-hdfs
hadoop-hdfs
hadoop3.x 新特性
纠删码(擦除编码)机制
HDFS为擦除编码(EC)提供了支持,以更有效地存储数据。与默认三个副本机制相比,EC策略可以节省约50%左右的存储空间。
但不可忽略的是编解码的运算会消耗CPU资源。纠删吗的编解码性能对其在HDFS中的应用起着至关重要的作用,如果不利用硬件方面的优化就很难得到理想的性能。英特尔的职能村粗加速库(ISA-L)提供了对纠删码编解码的优化,极大的提升了其性能。
纠删码是hadoop3.x新加入的功能,之前的hdfs都是采用副本方式容错,默认情况下,一个文件有3个副本,可以容忍任意2个副本(datanode)不可用,这样提高了数据的可用性,但也带来了2倍的冗余开销。例如3TB的空间,只能存储1TB的有效数据。而纠删码则可以在同等可用性的情况下,节省更多的空间,以RS-6-3-1024K这种纠删码策略为例子,6份原始数据,编码后生成3份校验数据,一共9份数据,只要最终有6份数据存在,就可以得到原始数据,它可以容忍任意3份数据不可用
查看当前支持的纠删码策略
hdfs ec -listPolicies

RS-10-4-1024k:使用RS编码,每10个数据单元(cell),生成4个校验单元,共14个单元,也就是说:这14个单元种,只要有任意的10个单元存在(不管是数据单元还是校验单元,只要总数=10),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576
RS-3-2-1024k:使用RS编码,每3个数据单元,生成2个校验单元,共5个单元,也就是说:这5个单元中,只要有任意的3个单元存在(不管是数据单元还是校验单元,只要总数=3),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576
RS-6-3-1024k:使用RS编码,每6个数据单元,生成3个校验单元,共9个单元,也就是说:这9个单元中,只要有任意的6个单元存在(不管是数据单元还是校验单元,只要总数=6),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576
RS-LEGACY-6-3-1024k:策略和上面的RS-6-3-1024k一样,只是编码的算法用的是rs-legacy
XOR-2-1-1024k:使用XOR编码(速度比RS编码块),每2个数据单元,生成1个校验单元,共3个单元,也就是说:这3个单元中,只要有任意的2个单元存在(不管是数据单元还是校验单元,只要总数=2),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576
设置纠删码策略
纠删码策略是与具体的路径(path)相关联的。也就是说,如果我们要使用删码,则要给一个具体的路径设置纠删码策略,后续,所有往此目录下存储的文件,都会执行此策略。
默认之开启对RS-6-3-1024k策略的支持,如要使用别的策略需要先启用
纠删码相关的命令
Usage: bin/hdfs ec [COMMAND]
[-listPolicies]
[-addPolicies -policyFile <file>]
[-getPolicy -path <path>]
[-removePolicy -policy <policy>]
[-setPolicy -path <path> [-policy <policy>] [-replicate]]
[-unsetPolicy -path <path>]
[-listCodecs]
[-enablePolicy -policy <policy>]
[-disablePolicy -policy <policy>]
[-help <command-name>]
hadoop-s-mapreduce
mapreduce
MapReduce概述
定义
MapReduce是一个分布式运算程序的编程框架,是用户开发,基于Hadoop的数据分析应用的核心框架
mapreduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群上
优缺点
优点
1 MapReduce易于编程
它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一摸一样的。就是因为这个特点使得MapReduce编程变得非常流行。
- 良好的扩展性
当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。
- 高容错性
MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,他可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是又Hadoop内部完成的。
- 适合PB级以上海量数据的离线处理
可以实现上前台服务器集群并发工作,提供数据处理能力
缺点
- 不擅长实时计算
MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果
- 不擅长流式计算
流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。
- 不擅长DAG(有向图)计算
多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个Map Reduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。
MapReduce核心思想

1. 分布式的运算程序往往需要分成至少2个阶段
2. 第一阶段的MapTask并发实例,完全并行运行,互不相干
3. 第二阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出
MapReduce进程
- 一个完整的MapReduce程序在分布式运行时有三类实例进程
1. MrAppMaster 负责整个程序的过程调度及状态协调
2. MapTask 负责Map阶段的整个数据处理流程
3. ReduceTask 负责Reduce阶段的整个数据处理流程
官方WordCount原码
采用反编译工具反编译源码,发现WordCount案例有Map类、Reduce类和驱动类。且数据的类型是Hadoop自身封装的序列化类型。
常用数据序列化类型
| Java类型 | Hadoop Writable类型 |
|---|---|
| Boolean | BooleanWritable |
| Byte | ByteWritable |
| Int | IntWritable |
| Float | FloatWritable |
| Long | LongWritable |
| Double | DoubleWritable |
| String | Text |
| Map | MapWritable |
| Array | ArrayWritable |
MapReduce编程规范
用户编写的程序分成三个部分:Mapper、Reducer和Driver
- Mapper阶段
1. 用户自定义的Mapper要继承自己的父类
2. Mapper的输入数据是KV对的形式(KV的类型可自定义)
3. Mapper种的业务逻辑写在map()方法种
4. Mapper的输出数据是KV对的形式(KV的类型可自定义)
5. map()方法(MapTask进程),对每一个<k,v>调用一次
- Reducer
1. 用户自定义的Reducer要继承自己的父类
2. Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
3. Reducer的业务逻辑写在reduce()方法种
4. ReduceTask进程对每一组相同k的<k,v>组调用一次reduce()方法
- Driver阶段
相当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是封装了MapReduce程序相关运行参数的job对象
WordCount案例实操
- 需求
在给定的文本文件种统计输出每一个单词出现的的总次数
创建一个项目
导入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
创建日志文件log4j2.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration status="error" strict="true" name="XMLConfig">
<Appenders>
<!-- 类型为Console,名称为必须属性-->
<Appender type="Console" name="STDOUT">
<!--布局为PatternLayout方式
输出样式为[INFO] [2018-14-23 18:2:21][org.test.Console]-->
<layout type="PatternLayout"
pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n"/>
</Appender>
</Appenders>
<Loggers>
<!--可加性为false-->
<Logger name="test" level="info" additivity="false">
<AppenderRef ref="STDOUT"/>
</Logger>
<!--root loggerConfig配置-->
<Root level="info">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</configuration>
创建WordCountMapper类
package lc.mapreduce.mapper.wordcount;
/**
* 编写一个MR程序,通常需要分三步
* 1. 编写 Mapper
* 2. 编写 Reducer
* 3. 编写 Driver
* <p>
* 插件类型性质的开发套路
* 1. 继承类或者实现接口
* 2. 实现或重写相关的方法
* 3. 提交执行
*/
/**
* 插件类型性质的开发套路
* 1. 继承类或者实现接口
* 2. 实现或重写相关的方法
* 3. 提交执行
*/
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;
/**
* 自定义Mapper的开发
* 1. 继承hadoop提供的Mapper类,提供输入和输出的kv的类型
* 2. 重写map方法
*
*
* Mapper 类的四个泛型 (以WordCount进行输出)
* KEYIN 输入数据的key类型 LongWritable 用来标识偏移量(从文件的哪个位置读取数据)
* VALUEIN 输入数据的value类型 Text 读取的一行数据
* KEYOUT 输出数据的key类型 Text 标示一个单词
* VALUEOUT 输出数据的value类型 IntWritable 一个单词出现的次数
*/
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
// 定义输出的key
private Text outk = new Text();
// 定义输出的value
private IntWritable outv = new IntWritable();
/**
* map 方法是整个mr中map阶段的核心处理方法
* 每读取一行数据,都要执行一次map方法
* @param key 表示偏移量
* @param value 读取的一行数据
* @param context 上下文对象,负责管理整个Mapper类中的方法的执行
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
// 1. 将读取的一行数据从Text转回String(方便操作)
String row = value.toString();
// 2. 按照分隔符进行分割
String[] wordArr = row.split(" ");
// 3. 将words进行迭代处理,把迭代的每个单词拼成kv输出
for (String word : wordArr) {
// 设置k v 的值
outk.set(word);
outv.set(1);
// 写出
context.write(outk, outv);
}
}
}
创建WordCountReducer类
package lc.mapreduce.mapper.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* 自定义Reducer类需要继承Hadoop提供的Reducer类,指定4个泛型
* KEYIN 输入数据的key的类型 Mapper 的输出类型
* VALUEIN 输入数据的value的类型
* <p>
* KEYOUT 输出数据的key的类型 表示一个单词
* VALUEOUT 输出数据的value的类型 表示单词出现的总次数
*/
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
// 定义输出的v
IntWritable outv = new IntWritable();
/**
* reduce 方法是mr中reduce阶段的核心处理过程
* <p>
* 多个相同key的kv对,会组成一组kv。 一组相同k的多个kv对,会执行一次reduce方法
*
* @param key 输入数据的key,表示一个单词
* @param values 当前key所对应的所有的value值
* @param context 上下文对象,负责调度整个reduce类中的方法的执行
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
// 1. 迭代values,取出每个value,进行汇总
int sum = 0;
for (IntWritable value : values) {
sum += value.get();
}
// 封装value
outv.set(sum);
// 数据写出
context.write(key, outv);
}
}
创建WordCountDriver类
package lc.mapreduce.mapper.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;
import java.io.IOException;
/**
* 驱动类,主要将写好的mr,封装成一个job对象,进行提交,然后执行
*/
public class WordCountDriver {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
// 1. 创建配置对象
Configuration conf = new Configuration();
// 2. 创建job对象
Job job = Job.getInstance(conf);
// 3. 关联驱动类
job.setJarByClass(WordCountDriver.class);
// 4. 关联Mapper 和 Reducer
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
// 5. 设置 mapper 输出的key和value
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
// 6. 设置最终输出的key和value的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
// 7· 设置输入和输出的路径
FileInputFormat.setInputPaths(job, new Path("D:\\input\\inputword"));
FileOutputFormat.setOutputPath(job, new Path("D:\\output"));
// 8. 提交job
job.waitForCompletion(true);
}
}
运行结果


其他
序列化

浙公网安备 33010602011771号