HDFS
Hadoop Distributed File System(分布式文件系统)
优点:
1.高容错性:数据自动保存多个副本,通过增加副本的形式,提高容错性。并且当某个副本丢失的时候,可以自动恢复。
2.适合处理大数据:可以存海量数据,GB级别,TB级别甚至PB级别。也可以存百万规模的文件数量。
3.可构建在廉价机器上。
缺点:
1.不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。(如果想要低延时的话,还是去使用MySQL吧)
2.无法高效的对大量小文件进行存储。如果是存储大量的小文件的话,那么NameNode需要去记录这些小文件的存储文件目录&块信息。那么这样就会大量占据NameNode的内存,而NameNode的内存是有限的。
3.不支持文件并发写入&不支持文件随机修改:一个文件同时只能是一个线程去写入,不支持多线程写,并且只能进行文件数据追加,不支持文件的随机修改。
NameNode
NameNode是Master,是集群的管理者。管理着:
1.配置副本策略 2.管理数据块Block映射信息 3.处理客户端的读写请求
DataNode
DataNode是Slave,NameNode下命令,DataNode执行实际的操作:
1.实际存储数据块 2.执行数据的读写操作
Secondary NameNode
并不是NameNode的热备份,他是辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode。在紧急情况下可以辅助恢复NameNode。
Client
1.Client负责将文件切分成一个个的文件块(企业通常是128M)。
2.与NameNode打交道,获取文件存储的目录位置信息。
3.与DataNode打交道,完成对于数据的读写操作。
4.Client通过一些命令来访问HDFS 以及 管理HDFS,比如NameNode格式化 以及 对HDFS增删改查操作。
HDFS文件块大小

所以其实需要保障的是,一秒能够传递一个文件块是最好的

为什么在HDFS中,Block块大小的设置和磁盘传输速率有关?
HDFS读写流程是什么?
1.HDFS Client:我首先会创建一个Distributed File System,我这里向NameNode请求上传文件,并且在请求中加上希望将文件上传到的目录路径。如:/lusiwei/zhenniubi/chenggong.txt
2.NameNode:NameNode检查两个事情,第一,你HDFS Client有没有权限;第二,检查目录结构(观察HDFS中,你指定的目录是否存在)。如果存在的话,也不可以,因为我这边HDFS里面已经有/lusiwei/zhenniubi/chenggong.txt这个文件了,你不能来给我做update修改啊。
3.HDFS Client:OK,你现在已经检查完我的权限了,并且也看了我指定的目录结构是不存在的,我是可以上传的了。那我这边就请求上传第一个Block,并且向你询问DataNode,我得问清楚你,我到底需要上传到哪个DataNode节点啊。
4.NameNode:返回DataNode1,DataNode2,DataNode3这三个节点,表示告诉HDFS Client,你可以采用这三个节点存储数据。
5.HDFS Client:那我开始建立Block传输管道了哈,这时候我创建FSDataOutputStream,首先我创建这个FSDataOutputStream这个流的时候,我是一个一个Chunk先准备好,然后再由Chunk组成Packet,组成Packet之后,开始往DataNode1传输。
传输入DataNode1的时候,我往DataNode1里面磁盘写一份,内存也写一份,DataNode1磁盘传输完的时候;DN1再向DN2写数据,同样也是往DN2磁盘写一份,内存写一份;然后DN2再向DN3建立起传输,过程一样,每一个DataNode被传输完数据,也就是说磁盘被写入数据之后,都会向客户端回复应答成功。
FSDataOutputStream还会创建一个ACK队列,ACK队列就是来接收DataNode的回复结果的,如果DataNode向客户端应答成功,就代表这个Packet传输完了,此次Packet传输成功了,那么ACK队列里面就会删除掉这个对应的Packet;如果传输失败了,那么ACK队列会重新将备份的这部分Packet写入到传输流当中,让传输流继续向DataNode传输这个之前失败过的Packet。
6.管道创建好了之后开始传输数据,传输数据的时候是以Packet(64KB)为单位去传输的,Packet又是由chunk组成的(chunk512byte+chunksum4byte,512字节的数据 + 4字节的校验位),有一个传输流,里面一个个Packet排列好等待被传输;然后还有一个ACK缓冲流,是一个备份,如果有哪个Packet传输失败了,会由ACK流补充上;如果Packet传输成功了,那么就会把ACK流里面的备份删掉。

HDFS节点距离:
节点距离:两个节点到达最近的共同祖先的距离总和

机架感知:副本存储节点的选择

HDFS读数据流程:

注意:在读数据的过程中肯定是选择三个副本中和客户端最近的那个节点的DataNode,但是不可能一直访问同一个节点,也会考虑到节点的负载均衡,所以可能也会去读别的节点。
NN和2NN工作机制:

Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,记录着HDFS文件系统的所有目录信息&文件inode的序列化信息
Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到Edits文件中。
seen_txid文件:保存的是一个数字,就是最后一个edits_的数字。
NN和2NN工作机制大概描述:
NameNode中有内存,同时也有edits_inprogress_001和fsimage,在客户端发出关于HDFS中数据的增删改请求的时候,这个更新操作的信息首先会被记录在edits_inprogress_001中,并不会将增删改操作立即写入到内存中。Secondary NameNode中有checkpoint机制,checkpoint机制有两个出发条件:1.定时的时间到了 2.Edits中的数据满了 这两个条件满足任何一个都会触发checkpoint机制的。(但是这里面有两个参数,一个是定时的时间,定时的时间一般是一个小时,如果一个小时到了,那就会触发checkpoint机制;另一个是Edits中的数据满了,默认一般是一百万次,如果Edits中的数据更新操作记录了一百万次,那就代表已经满了,在这个过程中,一般是一分钟查看一次Edits里面的数据量,查看到有一百万次了,就会直接触发checkpoint机制。)
一旦触发checkpoint机制,那么NameNode将会停止正在写的edit_inprogress_001文件,将edit_inprogress_001文件重命名成edits_001并且创建一个新的edit_inprogress_002文件来记录客户端传来的新的请求。将edits_001拷贝到Secondary NameNode中,同时也会将fsimage写到Secondary NameNode中。这时候Secondary NameNode会将 记录着所有更新操作的edits_001文件和fsimage文件加载到内存当中,生成新的fsimage.chkpoint文件(这个文件是实际操作了所有edits_001中的变更记录之后的fsimage文件)。
在这个过程中,NameNode中的edits_inprogress_002文件记录着所有客户端提交的新的变化信息(这个新指得是在edits_001文件被滚写到Secondary NameNode中之前)。但是请注意,Secondary NameNode中是没有edits_inprogress_002这个文件中记录的最新变更操作的。
Secondary NameNode会将fsimage.chkpoint文件序列化写入到本地磁盘中并且拷贝到NameNode中,fsimage.chkpoint文件会覆盖掉NameNode中的fsimage文件,但文件名字依然为fsimage。
结论:2NN上的FSImage总是比NN内存中的元数据慢一个Checkpoint周期。
DataNode工作机制:

DataNode工作机制大概描述:
DataNode启动后,会向NameNode注册,注册成功之后,会有两个周期性汇报动作给NameNode。
1.每周期(六个小时)会向NameNode上报所有块信息(块信息包括:数据,数据长度,校验和,时间戳),在向NameNode上报自己的所有块信息之前,会自己扫描一遍自己的块信息,然后立刻向NameNode汇报。
2.心跳测试:每周期(一般是3秒)会向NameNode返回带有NameNode给自己的命令,通知NameNode自己处于正常工作的状态。如果NameNode超过10分钟+30秒没有收到DataNode的心跳,则认为该节点不可用了。
数据完整性校验:

掉线时限参数设置:
默认值:2*dfs.namenode.heartbeat.recheck-interval+10*dfs.heartbeat.interval
一般是10分钟+30秒
面试重点:
1.HDFS文件块大小与磁盘读写速度关系
2.HDFS的一些shell命令
3.HDFS读写流程
4.HDFS小文件问题
5.NameNode 与 SecondaryNameNode工作机制
6.DataNode工作机制
MapReduce
优点:
1.良好的扩展性:可以动态增加服务器,解决计算资源不够的问题。
2.高容错性:任何一台机器挂掉,都可以将计算任务转移到其他节点。
缺点:
1.不适合实时计算 MySQL
2.不适合流式计算 SparkStreaming & Flink
3.不擅长DAG有向无环图计算。

总结一下:
数据被分成几个切片就有几个MapTask。
有几个分区,就有几个ReduceTask。
一个完整的MapReduce程序在分布式运行时有三类实例进程:
1.MrAppMaster:负责整个MapReduce程序的过程调度和状态协调
2.MapTask:负责Map阶段的整个数据处理流程
3.ReduceTask:负责Reduce阶段的整个数据处理流程
2和3统一称为yarn child

Mapper阶段:

1.用户自定义的Mapper要继承自己的父类
2.Mapper的输入数据是KV对的形式(KV类型可自定义)
3.Mapper中的业务逻辑写在map()方法中
4.Mapper的输出数据是KV对的形式(KV类型可自定义)
5.map()方法,也就是MapTask进程对每一个KV调用一次
Reducer阶段:
1.用户自定义的Reducer要继承自己的父类
2.Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
3.Reducer的业务逻辑写在reduce()方法中
4.ReduceTask进程对每一组相同k的kv组调用一次reduce()方法。
Driver阶段:
代码中Driver的7个部分:
1.创建Job
2.设置jar
3.关联Mapper和Reducer
4.设置Mapper的输出结果KV类型
5.设置最终数据输出的KV类型
6.设置数据的输入路径和输出路径
7.提交job()
Driver阶段也就是相当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是封装了MapReduce程序相关运行参数的job对象。
Hadoop序列化:
Hadoop序列化相比较Java序列化来说,更轻,更快,更方便。

企业中往往常用的序列化类型不能满足所有需求,比如在Hadoop框架内部传递一个bean对象等,那么该对象就需要实现序列化接口。
Hadoop中将数据序列化之后是如何传输到另一台服务器的呢?
MapReduce框架原理:

InputFormat数据输入
MapTask部分有一个非常重要的东西,叫做数据切片,数据切片会在逻辑上对数据进行分片,数据切片是MapReduce程序计算输入数据的单位,一个切片对应一个MapTask。(同时也有个东西叫做Block数据块,数据块是HDFS存储数据的单位,数据块是在物理上对数据进行分片)。
非常重要的内容要看下面,切片数决定MapTask并行度,也就是说我要是切了7片,那就有7个MapTask并行工作,我如果切了10片,那就有10个MapTask并行工作。

FileInputFormat切片源码解析(源码反复回去看):
1.程序先找到数据存储的目录
2.遍历处理目录下每一个文件(每一个文件循环一次处理,并不是所有数据叠加在一起,以文件为单位)
3.遍历第一个文件siwei.txt。
a. 首先获取文件大小
b. 计算切片大小
计算方式:computeSplitSize(Math.max(minSize,Math.min(maxSize,blockSize)));
mapreduce.input.fileinputformat.split.minsize = 1 默认值为1
mapreduce.input.fileinputformat.split.maxsize = Long.MAXValue 默认值Long.MAXValue
本地模式blockSize为 32M 集群模式blockSize为 256M
c. 默认情况下,切片大小 = blockSize 块大小
d. 每次切完之后,剩下的数据部分要计算是否大于块的1.1倍,如果大于块的1.1倍那么就继续 划。 分多块,如果不大于块的1.1倍那么就只划分一块。
e. 将切片信息写到一个切片规划文件中
f. 整个切片的核心过程在getSplit()方法中完成。
g. InputSplit只是记录了切片的元数据信息,比如起始位置,长度以及所在的节点列表等。
获取切片信息API:
//获取文件名称
String name = inputSplit.getPath().getName();
//根据文件类型获取切片信息
FileSplit inputSplit = (FileSplit) context.getInputSplit();
4.提交切片规划文件到Yarn上,Yarn上的MrAppMaster就可以根据切片规划文件计算开启MapTask个数。
TextInputFormat
FileInputFomMat实现类概念:
MapReduce程序运行的时候,输入的文件格式包括:基于行的日志文件,二进制格式文件,数据库表等。那么,针对不同的数据类型,MapReduce是如何读取这些数据的呢?
FileInputFormat常见的接口实现类包括:TextInputFormat,KeyValueTextInputFormat,NLineInputFormat,CombineTextInputFormat 和 自定义InputFormat等。
TextInputFormat是默认的FileInputFormat实现类。按行读取每条记录,键是存储该行在整个文件中的起始字节偏移量,LongWritable类型。值是这行的内容,不包括任何行终止符(换行符和回车符),Text类型。
CombineTextInputFormat:
框架默认的TextInputFormat切片机制是对任务按文件规划切片,不管文件多小,都会是一个单独的切片,都会交给一个MapTask,这样如果有大量小文件,就会产生大量的MapTask,处理效率很低。
1.应用场景:CombineTextInputFormat用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个MapTask处理。
2.虚拟存储切片最大值设置:CombineTextInputFormat.setMaxInputSplitSize(job,4194304);//4M
3.切片机制:
与TextInputFormat不同,CombineTextInputFormat生成切片的过程包括,虚拟存储过程和切片过程两部分。
这样的话是获得切片数量为3,如果是将MaxInputSplitSize设置为20M的话,那就是虚拟存储过程生成4个文件,切片过程生成1个大文件。
MapReduce工作流程大概描述

其中思维导图中的1-5为MapTask的过程,6为ReduceTask的过程。注:一般情况下是所有MapTask的流程都执行完了之后,ReduceTask才会开始工作。(Reduce也是从磁盘上去拉取数据的)
Shuffle机制
Map方法之后,Reduce方法之前的部分,就是Shuffle机制
Partition分区如何设置


想去看具体案例的话 回到视频中96,97,98
关于分区的总结:
-
ReduceTask的数量>getPartition结果数 那么会多产生几个空的输出文件part-r-000xx
-
1<ReduceTask的数量<getPartition结果数 那么会有一部分分区数据无法安放,报错IOException
-
ReduceTask的数量=1,无论如何都只产生一个分区,最终只有一个文件。
MapReduce中的排序分类
部分排序:传输给ReduceTask的每个文件内部是有序的
全排序:ReduceTask结束后,最终输出结果只有一个文件,且文件内部全部有序
二次排序:在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序
重写WritableComparable接口的compareTo方法,就可以实现排序。
想去看具体排序案例的话 会到视频中99,100,101,102
Combiner
上面说过了,后台线程(也就是负责溢写的线程),会将满足阈值的环形缓冲区中的内容溢写到磁盘中,Combiner所负责的就是,在溢写前先做一次预聚合。因此可以理解为,如果设置了Combiner,那么就是在每一次溢写前都会涉及到一次Combiner预聚合。
