大数据之Hadoop03_namenode对元数据的管理及MapReduce原理基础

元数据

元数据职责:
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方法取出下一条数据,这两个方法存在依赖关系,读取数据时需要联合使用.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值