第四次作业-Hadoop思想与原理
一、Hadoop起源与发展
Hadoop是道格·卡丁(Doug Cutting)创建的,Hadoop起源于开源网络搜索引擎Apache Nutch,后者本身也是Lucene项目的一部分。Nutch项目面世后,面对数据量巨大的网页显示出了架构的灵活性不够。当时正好借鉴了谷歌分布式文件系统,做出了自己的开源系统NDFS分布式文件系统。第二年谷歌又发表了论文介绍了MapReduce系统,Nutch开发人员也开发出了MapReduce系统。随后NDFS和MapReduce命名为Hadoop,成为了Apache顶级项目。
从Hadoop的发展历程来看,它的思想来自于google的三篇论文。
GFS:Google File System 分布式处理系统 ------》解决存储问题
Mapreduce:分布式计算模型 ------》对数据进行计算处理
BigTable:解决查询分布式存储文件慢的问题,把所有的数据存入一张表中,通过牺牲空间换取时间
二、名称节点、第二名称节点、数据节点
1.第一名称节点类似于数据目录。其主要有两大构件构成,FsImage和Editlog,FsImage用于存储元数据(长时间不更新、Editlog用于更新数据,但是随着时间推移,Editlog内存储的数据越来越多,导致运行速度越来越慢。
2.第二名称节点,当第一节点中Editlog到一个临界值时,HDFS会暂停服务,由第二节点将拷贝出Editlog,复制、添加到Fslmage后方并清空原Editlog的内容。这里有一点要注意这种备份是冷备份的形式,即没有实时性,需要停止服务,等数据恢复正常后继续使用。
3.数据节点是分布式文件系统HDFS的工作节点,负责数据的存储和读取,会根据客户端或者是名称节点的调度来进行数据的存储和检索,并向名称节点定期发送自己所存储的块的列表。

三、MapReduce运行原理
1、Map过程简述:
1)读取数据文件内容,对每一行内容解析成<k1,v1>键值对,每个键值对调用一次map函数
2)编写映射函数处理逻辑,将输入的<k1,v1>转换成新的<k2,v2>
3)对输出的<k2,v2>按reducer个数和分区规则进行分区
4)不同的分区,按k2进行排序、分组,将相同的k2的value放到同一个集合中
5)(可选)将分组后的数据重新reduce归约
2、reduce处理过程:
1)对多个Map的输出,按不同分区通过网络将copy到不同的reduce节点
2)对多个map的输出进行排序,合并,编写reduce函数处理逻辑,将接收到的数据转化成<k3,v3>
3)将reduce节点输出的数据保存到HDFS上
说明:
1)Mapper Task 是逻辑切分。因为Maper记录的都是block的偏移量,是逻辑切分,但相对于内存中他确实是物理切分,因为每个Mapper都是记录的分片段之后的数据。
2)shuffle是物理切分。MapReduce的过程是俩过程需要用到Shuffle的,1个mapper的Shufflle,1个多个reduce的Shuffle,一般每个计算模型都要多次的reduce,所以要用到多次的Shuffle。.

正常HDFS存储3份文件,Jar包默认写10份,NameNode通过心跳机制领取HDFS任务,运行完毕后JAR包会被删除。
Map端处理流程分析:
1) 每个输入分片会交给一个Map任务(是TaskTracker节点上运行的一个Java进程),默认情况下,系统会以HDFS的一个块大小作为一个分片(hadoop2默认128M,配置dfs.blocksize)。Map任务通过InputFormat将输入分片处理成可供Map处理的<k1,v1>键值对。
2) 通过自己的Map处理方法将<k1,v1>处理成<k2,v2>,输出结果会暂时放在一个环形内存缓冲(缓冲区默认大小100M,由mapreduce.task.io.sort.mb属性控制)中,当缓冲区快要溢出时(默认为缓冲区大小的80%,由mapreduce.map.sort.spill.percent属性控制),会在本地操作系统文件系统中创建一个溢出文件(由mapreduce.cluster.local.dir属性控制,默认${hadoop.tmp.dir}/mapred/local),保存缓冲区的数据。溢写默认控制为内存缓冲区的80%,是为了保证在溢写线程把缓冲区那80%的数据写到磁盘中的同时,Map任务还可以继续将结果输出到缓冲区剩余的20%内存中,从而提高任务执行效率。
3) 每次spill将内存数据溢写到磁盘时,线程会根据Reduce任务的数目以及一定的分区规则将数据进行分区,然后分区内再进行排序、分组,如果设置了Combiner,会执行规约操作。
4) 当map任务结束后,可能会存在多个溢写文件,这时候需要将他们合并,合并操作在每个分区内进行,先排序再分组,如果设置了Combiner并且spill文件大于mapreduce.map.combine.minspills值(默认值3)时,会触发Combine操作。每次分组会形成新的键值对<k2,{v2...}>。
5) 合并操作完成后,会形成map端的输出文件,等待reduce来拷贝。如果设置了压缩,则会将输出文件进行压缩,减少网络流量。是否进行压缩,mapreduce.output.fileoutputformat.compress,默认为false。设置压缩库,mapreduce.output.fileoutputformat.compress.codec,默认值org.apache.hadoop.io.compress.DefaultCodec。
Reduce端处理流程分析:
1) Reduce端会从AM那里获取已经执行完的map任务,然后以http的方法将map输出的对应数据拷贝至本地(拷贝最大线程数mapreduce.reduce.shuffle.parallelcopies,默认值5)。每次拷贝过来的数据都存于内存缓冲区中,当数据量大于缓冲区大小(由mapreduce.reduce.shuffle.input.buffer.percent控制,默认0.7)的一定比例(由mapreduce.reduce.shuffle.merge.percent控制,默认0.66)时,则将缓冲区的数据溢写到一个本地磁盘中。由于数据来自多个map的同一个分区,溢写时不需要再分区,但要进行排序和分组,如果设置了Combiner,还会执行Combine操作。溢写过程与map端溢写类似,输出写入可同时进行。
2) 当所有的map端输出该分区数据都已经拷贝完毕时,本地磁盘可能存在多个spill文件,需要将他们再次排序、分组合并,最后形成一个最终文件,作为Reduce任务的输入。此时标志Shuffle阶段结束,然后Reduce任务启动,将最终文件中的数据处理形成新的键值对<k3,v3>。
3) 将生成的数据<k3,v3>输出到HDFS文件中。

Map与Reduce执行过程图
三 Hadoop序列化--Writable
序列化就是将内存当中的数据序列化到字节流中,
他实现了WritableComparable 接口,并继承了Writable(Write和ReadFile需要被实现)和Compare接口
1 特点:
1 )紧凑:高校使用存储空间
2 )快速:读写数据的额外开销小
3 )可扩展:可透明的读取老格式的数据
4 )互操作:支持多语言的交互
说明:JAVA 的序列化对继承等的结构都保存了,而对hadoop用不着,只需要存储字符就可以,所以有自己的机制。
HDFS读写流程
hdfs的读写主要设计Client、NameNode、DataNode等节点

1.打开HDFS文件,构造DFSInputStream输入流
HDFS客户端调用DistributesFileSystem.open()方法打开HDFS文件,其底层实际上是调用ClientPropocol.open()方法,返回一个HdfsDataInputStream(DFSInputStream的装饰类,真正进行读取操作是DFSInputStream)。
2.从NameNode端获取数据存储的DataNode地址
DFSInputStream的构造方法中,会调用ClientProtocol.getBlockLocations()方法获取该JDFS文件起始位置数据块的位置信息。NameNode会按照储存位置与客户端的距离排序选择最优的一个DataNode节点。
3.建立数据连接(流式接口)
DFSInputStream.read()方法从数据节点读取数据,数据会以数据包(packet)为单位从数据节点传送到客户端。当一个数据块读取结束,则DFSInputStream会重新调用ClientProtocol.getBlockLocations()获取文件的下一个数据块位置,并与其最优节点建立连接,重复以上动作。
4.关闭输入流
当客户端完成文件读取后,通过HdfsDataInputStream.close()方法关闭输入流。
另外数据块的应答包中除了包含数据还包含校验码,HDFS收到数据后会进行校验,如果发生错误,则会通过ClientProtocol.reportBadBlocks()向名字节点汇报损坏的数据块的信息,并且DFSInputStream会切换到另外一个保存该数据块的节点读取文件。

1.创建文件
client发起文件上传请求,调用DistributedFileSystem对象的create方法,在HDFS系统中创建一个新的空文件,该方法在底层调用ClientProtocol.creat()方法通过RPC与NameNode建立连接,NameNode检查目标文件是否已经存在,父目录是否存在,并检查用户是否有相应的权限,若检查通过,Namenode会在文件系统目录树下的指定目录下创建一个新文件文件,但未申请任何Block,并将该操作记录在editlog中,否则的话文件创建失败,客户端得到异常信息
2.获取HdfsDataOutputStream对象
ClientProtocol.creat()调用结束后,DistributedFileSystem.create()方法会返回一个HdfsDataOutputStream(DFSOutputStream的包装类,真正起作用的是DFSOutputStream)
3.获取Block信息及信息流管道
这时,名字节点只是在目录上创建一个空文件,DFSOutputStream会调用ClientProtocol.addBlock()向NameNode获取资源,NameNode根据配置文件中指定的备份(replica)数量及机架感知原理进行文件分配,返回LocatedBlock对象,其记录了保存该数据块的节点的信息,就DFSOutputStream会建立数据管道写数据块了。以三台DataNode为例:A B C。注: Hadoop在设计时考虑到数据的安全与高效,数据文件默认在HDFS上存放三份,存储策略为:第一个备份放在客户端相同的datanode上(若客户端在集群外运行,就随机选取一个datanode来存放第一个replica),第二个replica放在与第一个replica不同机架的一个随机datanode上,第三个replica放在与第二个replica相同机架的随机datanode上,如果replica数大于三,则随后的replica在集群中随机存放,Hadoop会尽量避免过多的replica存放在同一个机架上.选取replica存放在同一个机架上.(Hadoop 1.x以后允许replica是可插拔的,意思是说可以定制自己需要的replica分配策略)
4.成功建立数据管道后,HDFS客户端可以开始写数据,写入DFSOutputStream的数据会被缓存在数据流中,以数据包(packet)的形式存在,写满一个packet对象则将其放在输出队列中(dataQueue)中,等待DataStream线程处理。
5.DataStream线程处理dataQueue中的数据
首先会进行错误处理(处理方式主要为三步:关闭当前IO流、将ackQueue中等待确认的数据包全部移到dataQueue中、重新初始化数据流管道进行写入),之后等待dataQueue写满,然后在第一个datanode上建立数据流管道,发送数据包。当写完当前Block后会重新申请Block.例如:client向请求的3台的DataNode中的A上传数据,(本质是一个RPC调用,建立pipeline),A收到请求会继续调用B,然后B调用C,将整个pipeline建立完成后,逐级返回client。client开始往A上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位(默认 64K),A收到一个packet就会传给B,B传递给C;A每传一个packet会放入一个应答队列等待应答,如果数据包都得到确认则删除应答队列中的数据包。
6.关闭输入流并提交文件
当所有数据上传完成后,可以调用close()方法关闭输入流,并调用Clientprotocol.complete()告诉NameNode文件写入完成。
有可能管道线中的多个datanode宕掉(一般这种情况很少),但只要dfs.relication.min(默认值为1)个replica被创建,我么就认为该创建成功了,剩余的relica会在以后异步创建以达到指定的replica数。当一个block传输完成后,client再次发送请求NameNode上传第二个block到服务器

4.梳理HBase的结构与运行流程,以用图与自己的话进行简要描述,图中包括以下内容:
- Master主服务器的功能

A包含访问HBase的接口,同时在缓存中维护着已经访问过的Region位置信息,用来加快后续数据访问过程 B可以帮助选举出一个Master作为集群的总管,并保证在任何时刻总有唯一一个Master在运行 C主要负责表和Region的管理工作 D是HBase中最核心的模块,负责维护分配给自己的Region,并响应用户的读写请求
- Region服务器的功能

- Zookeeper协同的功能


集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户.
- Client客户端的请求流程

5.理解并描述Hbase表与Region与HDFS的关系。



HDFS是GFS的一种实现,他的完整名字是分布式文件系统,类似于FAT32,NTFS,是一种文件格式,是底层的,Hadoop HDFS为HBase提供了高可靠性的底层存储支持。
HBase是Google Bigtable的开源实现,类似Google Bigtable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统

6.理解并描述Hbase的三级寻址。

系统如何找到某个row key (或者某个 row key range)所在的region
bigtable 使用三层类似B+树的结构来保存region位置。
第一层: 保存zookeeper里面的文件,它持有root region的位置。
第二层:root region是.META.表的第一个region其中保存了.META.表其它region的位置。通过root region,我们就可以访问.META.表的数据。
第三层: .META.表它是一个特殊的表,保存了hbase中所有数据表的region 位置信息。
说明:
(1) root region永远不会被split,保证了最需要三次跳转,就能定位到任意region 。
(2).META.表每行保存一个region的位置信息,row key 采用表名+表的最后一行编码而成。
(3) 为了加快访问,.META.表的全部region都保存在内存中。
(4) client会将查询过的位置信息保存缓存起来,缓存不会主动失效,因此如果client上的缓存全部失效,则需要进行最多6次网络来回,才能定位到正确的region(其中三次用来发现缓存失效,另外三次用来获取位置信息)。
7.假设.META.表的每行(一个映射条目)在内存中大约占用1KB,并且每个Region限制为2GB,通过HBase的三级寻址方式,理论上Hbase的数据表最大有多大?
一个-ROOT-表最多只能有一个Region,也就是最多只能有2GB,按照每行(一个映射条目)占用1KB内存计算,2GB空间可以容纳2GB/1KB= 2^33行,也就是说,一个-ROOT-表可以寻址2^33个.META.表的Region
·同理,每个.META.表的Region可以寻址的用户数据表的Region个数2GB/1KB= 2^33·最终,三层结构可以保存的Region数目是(2GB/1KB)×(2GB/1KB)= 2^66Region。
8.MapReduce的架构,各部分的功能,以及和集群其他组件的关系。
MapReduce执行步骤:
①用户编写MapReduce程序通过client提交jobtrasker
②jobstrasker开始检查tasktrasker健康情况,将client任务交给暂时空闲的tasktrasker执行
③jobstrasker并将各个tasktrasker工作状态和健康情况发送给taskscheduler
④根据任务情况开始分配资源给map task执行map shuffle和reduce task 执行reduce shuffle。
⑤map task向jobstrasker反应数据归并、拆分是否执行完成
⑥reduce task向jobstrasker请求,若map task执行完成,则调用数据进行计算
⑦输出计算结果以HDFS文件格式 。
Shuffler的执行过程
(一)Map shuffle执行过程
①输入一个个切片split(一般我们默认一个block对应一个split)
②将split数值存入到Map里面,以键值对形式存储
③对map里面的值进行排序
④对map里面的值进行归并
⑤输出一个整合的map
(二)Reduce shuffle执行过程
①接收各个map整合后的数据
②对各个map里面的相同的K值归并到一起,并对其排序
③调用一个reduce对其归并后的map进行计算
④输出一个计算结果HDFS文件
(三)Shuffler的执行过程
①输入一个个切片split(一般我们默认一个block对应一个split)
②将一个个split数值存入到一个个Map里面,以键值对形式存储
③对各个map里面的值进行排序
④对各个map里面的值进行归并
⑤将不同map里面相同K值归并在一起并排序,形成新的map
⑥调用一个reduce对其归并后的map进行计算
⑦输出一个计算结果HDFS文件

浙公网安备 33010602011771号