大数据面试集锦---Hadoop篇

新手大数据开发一枚,之前也做了一些MR、Spark、Storm的项目,但是有些框架本身的东西还不是很懂,毕业季找工作,就顺手整理一些东西。希望在这个过程中能把只是融会贯通。不会很细的去面面俱到的写框架,只会写一写自己遇到的问题。

HDFS的设计思想
(1)一次写入,多次读取
(2)hdfs是对搞数据吞吐量优化的,以提高时间延时为代价
(3)namenode存储着文件系统的元数据(每个目录、每个文件和数据块的存储信息)
Hadoop2.x默认block size增大为128M,这样做有什么好处?
(1)减轻了namenode的压力
Hadoop集群在启动的时候,会向namenode上报自己的block信息。namenode会把这些信息存在内存中。如果块变大了,那么namenode记录的信息相对减少,所以namenode就有更大的内存去做其他的事情,使得整个集群的性能增强。
(2)增大带来的影响?
这个应该不是问题,可以灵活设置。
如果数据量级别达到PB的话,可以设置的大一些。
负面影响:如果网络环境不好,可能会造成重新传输。
引申:Namenode和Datanode
NameNode:namenode管理文件系统的命名空间。它维护文件系统树以及整棵树内所有的文件和目录。这些信息是永久保存在本地磁盘的,以两个文件的形式:命名空间镜像文件和编辑日志文件同时,NameNode会也记录着每个文件中各个块所在的数据节点信息,但是这些文件不是永久保存的,这些信息会在系统启动的时候由数据节点重建。
DataNode:根据需要存储并检索数据块,并定期向namenode的发送存储的数据块的列表
NameNode的灾备:运行一个辅助namenode的,但不会被用作名称节点(Secondary NameNode)。这个节点一般在另一台单独的物理计算机(内存需求与NameNode相同)上运行。其作用是通过编辑日志合并并命名空间镜像,以防编辑日志过大。过大的日志文件会使NameNode在下次重新启动时需要更大的时间.Secondary NameNode会在NameNode发生故障时生效。但是其保存的状态也总是滞后于NameNode的,所以在NameNode全部失效时,会有部分数据的丢失。
小文件Block块占用问题:
小于块大小的小文件不会占用整个HDFS块空间。较多的小文件会占用更多的NameNode的内存。在Linux File system占用的空间为实际文件的大小,不是一个块的大小。
HDFS设置Block的目的(为什么默认Block设置为128M):
(1)减少硬盘寻道时间:HDFS是支持大容量流式读写操作的,当涉及大数据量时,Block size设置的越小,需要读取的数据块就越多。而数据块在硬盘上是非连续存储的,普通硬件移动磁头,随机寻址较慢,读越多的数据块就增大了总的硬盘寻址时间。合适的块大小有助于减少硬盘寻道时间,提高系统吞吐量。
联邦HDFS
NameNode在内存中保存文件系统中每个文件和每个数据块的引用关系这意味着对于一个拥有大量文件的超大集群来说,内存将成为限制系统横向拓展的瓶颈。在Hadoop2.x中引入的联邦HDFS允许系统通过添加的NameNode实现拓展,其中每个NameNode的管理文件系统命名空间中的一部分。在联邦环境下,每个NameNode会维护一个命名空间卷,包括命名空间的源数据和在下的文件的所有数据块的。命名空间卷之间是相互独立的,两两之间并不相互通信,甚至其中一个的NameNode的失效也不会影响由其他的NameNode维护的命名空间的可用性。数据块池不再切分,集群中的DataNode需要注册到每个NameNode,并且存储着来自多个数据块池中的数据块。
Client端上传文件
Client向NameNode发起文件写入的请求.NameNode根据文件大小和文件块配置情况,返回给客户端所有者管理部分Dat aNode的信息.Client将文件划分为多个Block,根据DataNode的地址信息,按顺序写入每一个DataNode块中。
Hadoop环境搭建
配置文件conf目录下的:core-site.xml中; HDFS-site.xml中; mapred-site.xml中
写入HDFS文件
client链接NameNode,确定文本起始块的位置。NameNode返回存有该块副本的DataNode地址。
这些DataNode根据他们与客户端的距离来排序(根据集群的网络拓扑)。
client通过HDFS的API将数据块存储到DataNode上。
DataNode将数据水平备份。并且备份完将反馈给client
client通知NameNode存储块完毕。
NameNode将元数据同步到内存中。
读取HDFS文件
1、client链接NameNode,查看元数据,找到数据的存储位置
2、client通过HDFS的API并发读取数据
3、并发连接
Hadoop中combiner的作用
每一个map都会有一个本地输出。combiner的作用是对map的输出先做一次合并,以减少map和Reduce间的数据通信量(减少网络IO)。combiner的输出是Reduce的输入,这一步骤绝不会改变最终的结果。
Hadoop partition的作用
partition的默认实现是hashpartition,是map端将数据按照reduce个数取余,进行分区,不同的reduce来copy自己的数据。
partition的作用是将数据分到不同的reduce进行计算,加快计算效果。
简述Hadoop搭建的过程
1、新建Hadoop用户,并赋予sudo权限
2、配置Java环境
3、配置/etc/hosts 主机与IP的
4、配置免密
5、解压Hadoop版本包
6、配置Hadoop conf目录下的hadoop -env.sh,core-site.xml,hdfs-site.xml,mapre-site.xml
7、配置Hadoop的环境变量
8、hadoop namenode -format
9、.bin/start-all.sh
Hadoop 杀死一个job
hadoop job -list (拿到job ID)
hadoop job -kill job-id
hadoop 加载一个新的存储节点和删除一个计算节点
动态添加DataNode,不停用namedode方式:
1、修改slaves文件,添加需要增加的节点host或者IP,并将其更新到各个节点
2、在DataNode中执行启动DataNode命令。
sh hadoop-daemon.sh start datanode
在NameNode
3、可以通过界面查看节点添加情况。
删除一个存储节点:
hadoop dfsadmin -refreshNodes
hadoop dfsadmin -report
Hadoop 作业调度器
调度器基本作用:根据节点资源使用情况和作业的要求,将任务调度到各个节点上执行
调度器考虑的因素:作业提交时间、作业优先级、作业所在队列的资源限制。
大数据面试集锦---Hadoop篇
文章图片

1、client通过submitJob()函数向jobTracker提交一个作业
2、Jobtracker通知taskSechduler,进行初始化,创建一些内部函数
3、tasktracker通过心跳向jobtracker汇报它的资源情况,比如空闲的map slot和Reduce slot
4、如果jobtracker发现第一个tasktracker有空闲的资源,JobTracker 就会调用 TaskScheduler的assignTasks() 函数,返回一些task list给第一个TaskTracker。 这时TaskTracker就会执行调度器分配的任务。
Hadoop自带调度器:
1、先进先出调度器(FIFO)
2,容量调度器:支持多个队列,每个队列可配置一定的资源量,每个队列采用FIFO调度策略,为了防止同一个用户的作业独占队列中的资源,该调度器会对同一用户提交的作业所占的资源进行限制。
3,公平调度器:公平调度,所有工作具有相同的资源
mapreduce如何解决数据倾斜
数据倾斜:map / reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个减少节点运行很慢,导致整个程序的处理时间很长,这是因为某一个的条键数比其他键多很多(有时是百倍或者千倍之多),这条关键所在的减少节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完,此称之为数据倾斜。
解决方法:自己实现分区类,用键和值相加取散列值
源码: public int getPartition(K key,V value, int numReduceTasks){ 返回(key.hashCode()&Integer.MAX_VALUE的)%numReduceTasks; } 修改后: public int getPartition(K key,V value, int numReduceTasks){ return (((key).hashCode()+value.hashCode()) & Integer.MAX_VALUE) % numReduceTasks; } 或者: public class HashPartitioner extends Partitioner { private int aa= 0; /** Use {@link Object#hashCode()} to partition. */ public int getPartition(K key, V value, int numReduceTasks) { return (key.hashCode()+(aa++) & Integer.MAX_VALUE) % numReduceTasks; }

Hadoop框架的优化:
(1) 从应用程序角度进行优化。由于mapreduce是迭代逐行解析数据文件的,怎样在迭代的情况下,编写高效率的应用程序,是一种优化思路。
(2) 对Hadoop参数进行调优。当前hadoop系统有190多个配置参数,怎样调整这些参数,使hadoop作业运行尽可能的快,也是一种优化思路。
(3) 从系统实现角度进行优化。这种优化难度是最大的,它是从hadoop实现机制角度,发现当前Hadoop设计和实现上的缺点,然后进行源码级地修改。该方法虽难度大,但往往效果明显。
(4)linux内核参数调整
从应用程序角度进行优化
(1) 避免不必要的reduce任务
如果mapreduce程序中reduce是不必要的,那么我们可以在map中处理数据, Reducer设置为0。这样避免了多余的reduce任务。
(2) 为job添加一个Combiner
为job添加一个combiner可以大大减少shuffle阶段从map task拷贝给远程reduce task的数据量。一般而言,combiner与reducer相同。
(3) 根据处理数据特征使用最适合和简洁的Writable类型
Text对象使用起来很方便,但它在由数值转换到文本或是由UTF8字符串转换到文本时都是低效的,且会消耗大量的CPU时间。当处理那些非文本的数据时,可以使用二进制的Writable类型,如IntWritable, FloatWritable等。二进制writable好处:避免文件转换的消耗;使map task中间结果占用更少的空间。
(4) 重用Writable类型
很多MapReduce用户常犯的一个错误是,在一个map/reduce方法中为每个输出都创建Writable对象。例如,你的Wordcout mapper方法可能这样写:
public void map(...) { …for (String word : words) { output.collect(new Text(word), new IntWritable(1)); } }

这样会导致程序分配出成千上万个短周期的对象。Java垃圾收集器就要为此做很多的工作。
更有效的写法是:
class MyMapper ... { Text wordText = new Text(); IntWritable one =new IntWritable(1); public void map(...){ for(String word:words){ wordText.set(word); output.collect(wordText,one); } } }

(5)使用StringBuffer而不是String
mapreduce工作流程
mapreduce实际的处理过程可以理解为Input->Map->Sort->Combine->Partition->Reduce->Output。
【大数据面试集锦---Hadoop篇】1、Input
数据以一定的格式传递给Mapper,有TextInputFormat等可以使用
2、map阶段
对输入的(key,value)进行处理
3、Sort阶段
对Map的输出进行排序,map的输出会放到一个缓冲区中,在缓冲区进行与排序。如果数据过大,超出缓冲区大小,会有个溢写到磁盘过程。
4、Combiner
这个阶段对于sort之后有相同key的结果进行合并
5、Partition阶段
将mapper的中间结果按照key的范围划分为R份,按照key分别映照给不同的Reduce。partition的作用就是把这些数据归类
6、Reduce阶段
对于mapper阶段的结果进行进一步处理
7、output阶段
Reduce输出数据的格式

    推荐阅读