元数据
元数据职责:
1.维护虚拟访问目录
2.储存数据块信息.副本个数.物理块的位置
3.储存块描述信息,起始位置,大小
namenode对元数据的管理
1.客户端在发起读取数据的请求时,需要元数据要在很高的效率下找出数据储存的位置,所以元数据储存在内存中,并以Tree型数据结构储存,但储存设备宕机后,内存中的数据会消失,所以元数据就也会写入到磁盘中,做持久化储存.因此在内存中和磁盘中各有一份元数据.
2.内存中的元数据称之为FSImage(类),序列化(持久化)到磁盘中后变成image文件
3.但如果频繁的对HDFS系统操作,就会导致内存中的数据频繁的往内存中写入,也会浪费大量的内存资源,于是便有了日志系统(edit),日志会记录客户端的操作记录,这样就可以固定时间,将内存中的数据序列化(持久化)写入磁盘中生成image文件,这样重启namenode时就会从磁盘和日志中读取元数据到内存中,保证数据的完整性.(生成的日志是动态滚动追加的)
4.但随着客户端操作的增多,日志文件会越来越多,每次开机的时间会变得越来越长,为解决此问题,便有了secondray namenode ,用来处理对元数据的储存问题(checkpoint机制).
5.secondray namenode会每隔1h或客户端进行了100万次操作,去namenode中下载image文件和edit日志文件,将image的镜像文件反序列化到secondray namenode的FSImage类中,并将edit镜像日志也加载到FSImage类中,然后序列化整合成一个新的image镜像文件,并上传到namenode磁盘中,这样下次namenode启动时,就只需要加载这个镜像image和未下载到secondray namenode的日志,就可以恢复元数据到内存中,减少开机的时间.
6.生成镜像image后,没用的image文件和日志会被清理.
HDFS的高可用原理(HA原理)
hadoop高可用可取代SecondaryNamenode
引用:https://blog.csdn.net/u012736748/article/details/79534019
MapReduce
Mapreduce是一个运算框架,可以分析处理hdfs中的文件,也可以处理本地的文件
MR设计思想,当我们需要处理一个大文件时,单机处理起来,速度较慢,效率很低,于是我们可以将大文件进行切块,分布到集群中,分布式并行处理,可以大大加快处理效率
例如我们要处理一个大文件,统计一个文件中每个单词出现个数,我们可以使用MapReduce框架进行处理,该框架分为两个阶段,一个是Map,一个是Reduce
1.Map阶段
1)因为文件较大,在客户端该文件会按默认大小被切分成n块,然后被分布到各个节点中,并行处理,按我们想要实现的代码逻辑,处理出相应的结果
2)各节点处理完后会将结果写入到框架的缓存区中,然后会按启动类设定的的任务个数要求进行任务划分,从而可以分布到不同的节点上进行聚合运算,这个过程叫做分区,想分几个任务,就会分成几个区,分区时会将数据按一定的规则默认排序,分区排序后发送到Reduce阶段.
2.Reduce阶段
1)接收到缓存区发送过来的数据,会并发进行聚合运算,但聚合运算前还会自动进行数据分组,分组会将同一key值的数据的value值划分到同一组,然后进入到方法里面执行代码逻辑(聚合运算),每组数据会执行一次聚合方法,执行完后,将结果输出到本地文档中.
入门代码实现
/**
* Map
* Map类需要先继承Mapper类,用以读取文本内容,每次读取一行,使用代码实现切割开每个单词输出到缓存区,缓存区会自动在分布式运算框架中进行分区,排序然后发送到Reduce
* Mapper需要有四个参数范型参数:
* 参数1:输入每行数据的偏移量Long ---封装(Hadoop自己的序列化方式)---> LongWritable
* 参数2:输入每行数据的文本内容String ---封装(Hadoop自己的序列化方式)---> Text
* 参数3:输出切分后的每个单词String ---封装(Hadoop自己的序列化方式)---> Text
* 参数4:输出切分后每个的数量(1)Integer ---封装(Hadoop自己的序列化方式)---> IntWritable
*/
class WordMap extends Mapper<LongWritable, Text,Text, IntWritable> {
/**
* 重写map方法,书写切分逻辑,此方法,每读一行执行一次
* 括号内参数:
* @param key -->输入每行数据的偏移量Long
* @param value -->输入每行数据的文本内容String
* @param context -->全局对象,衔接上下文
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//将Text类型转成String类型
String line = value.toString();
//按\\s+切分
String[] split = line.split("\\s+");
//得到每个单词,按单词为key,数量1为value输出到缓存区
for (String word : split) {
context.write(new Text(word),new IntWritable(1));
}
}
}
/**
* Reduce
* Reduce类需要继承Reduer类,将输入的分好区的,排好序的数据,先自动进行分组操作
* 范型参数1:输入进来的单个单词
* 范型参数2:输入进来的单个单词数量(1)
* 范型参数3:想要输出的单词
* 范型参数4:聚合后的单词数量
*/
class WordReduce extends Reducer<Text, IntWritable,Text, IntWritable> {
/**
* 重写reduce方法将分好组的单词进行需求的聚合运算,此方法每来一组就会执行一次
* 括号内参数:
* @param key 分组后的单词
* @param values 分组后同组所有单词数量的一个迭代器
* @param context 全局对象,衔接上下文
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int i =0;
for (IntWritable value : values) {
i++;
}
context.write(key,new IntWritable(i));
}
}
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
/**
* 启动类
*/
public class WordTest {
public static void main(String[] args) throws Exception {
//获取conf配置对象
Configuration conf = new Configuration();
//获取执行任务的工作类,传入conf参数,和工作名字(任起)
Job job = Job.getInstance(conf, "w");
//设置任务数量,有几个任务,就会输出几个文件
job.setNumReduceTasks(2);
//设置Map类来源
job.setMapperClass(WordMap.class);
//设置Reduce类来源
job.setReducerClass(WordReduce.class);
//设置Map类输出的key
job.setMapOutputKeyClass(Text.class);
//设置Map类输出的value
job.setMapOutputValueClass(IntWritable.class);
//设置Reduce类输出的key
job.setOutputKeyClass(Text.class);
//设置Reduce类输出的value
job.setOutputValueClass(IntWritable.class);
//设置文件读取的路径
FileInputFormat.setInputPaths(job,new Path("D:\\1.txt"));
//设置结果写出的路径
FileOutputFormat.setOutputPath(job,new Path("D:\\1\\"));
//任务执行完的标记,如果b为true表示执行完毕
boolean b = job.waitForCompletion(true);
}
}
补充:
在RM进行分布式运算时,为提高运算效率,会遵循以下两个运算规则:
1.移动运算优于移动数据;即移动代码去运算,优于移动数据到另一个节点
2.本地运算优于跨节点运算
以上两个优于都是避免数据走网络,当然都是在内存充足的情况下才会选择执行
序列化:将内存中的对象转换成二进制持久化储存到磁盘中的过程:
1.我们经常使用的序列化有直接序列化一个对象 例如: oos.writeObject(user); 但这种序列化会浪费大量的内存.
2.转成JSON格式,序列化到磁盘中,例如:JSON.toJSON(user);但依然会占用多余的内存
3.在hdfs中会使用第三中序列化格式,直接将对象的属性所对应的值写入到磁盘中
例如(注意写入的顺序,要与定义属性时的顺序保持一致):
oos.writeUTF(name);
oos.writeInt(age);
oos.writeDouble(sal);
迭代器:迭代器是可以用于遍历各种数据类型的方法,它可以把访问逻辑从不同类型的数据类中抽象出来,从而避免向客户端暴露数据的内部结构,遍历数据中的每个对象而不用考虑容器中对象的个数,而且保护数据不显示的表现出来。
迭代器中只有一个对象,该对象通过它的hasNext方法判断是否还有下一条数据,然后通过next方法取出下一条数据,这两个方法存在依赖关系,读取数据时需要联合使用.