文章目录
项目地址 https://github.com/datawhalechina/juicy-bigdata,感谢项目团队的付出。
本次学习任务是完成
期中大作业
。
6. 1 面试题
6.1.1 简述Hadoop小文件弊端
过多的小文件会加重 namenode
负担,增加寻址成本,降低处理效率,最终导致集群整体性能下降。
6.1.2 HDFS中DataNode挂掉如何处理?
HDFS namenode
与 HDFS datenode
具有周期性心跳通信机制,检查发现某个数据的副本数量小于冗余因子时,说明有 datanode
节点宕机,HDFS
就会启动数据冗余复制,为它生成新的副本。
登录故障节点,执行如下命令:
hdfs --daemon start datanode
6.1.3 HDFS中NameNode挂掉如何处理?
当 HDFS namenode
宕机时,首先到远程挂载的网络文件系统中获取备份的元数据信息,放到第二名称节点上进行恢复,并把第二名称节点作为名称节点来使用。
6.1.4 HBase读写流程?
- hbase 写流程
1、客户端写入数据的流程
1)由客户端发起写数据请求,首先会与zookeeper建立连接
2)从 zookeeper 中获取 hbase:meta 表被哪一个 regionserve所管理
3)连接 hbase:meta 表中获取对应的 regionserver 地址 (从meta表中获取当前要写入数据的表对应的region所管理的regionserver) 只会返回一个 regionserver 地址
4)与要写入数据的 regionserver 建立连接,然后开始写入数据,将数据首先会写入到HLog,然后将数据写入到对应store模块中的memstore中
(可能会写多个),Hlog
写入磁盘后,memstore
开始写入磁盘,写入完毕后,表示 hbase 数据写入完成。
2、服务器内部的操作
异步操作
5)随着客户端不断地写入数据,memstore中的数据会越来多,当内存中的数据达到阈值(128M/1h)的时候,放入到blockchache中,生成新的memstore接收用户过来的数据,然后当blockcache的大小达到一定阈值(0.85)的时候,开始触发flush机制,将数据最终刷新到HDFS中形成小的Hfile文件。
6)随着不断地刷新,storefile不断地在HDFS上生成小HFIle文件,当小的HFile文件达到阈值的时候(3个及3个以上),就会触发Compaction机制,将小的HFile合并成一个大的HFile.
7)随着不断地合并,大的HFile文件会越来越大,当达到一定阈值(最终10G)的时候,会触发分裂机制(split),将大的HFile文件进行一分为二,同时管理这个大的HFile的region也会被一分为二,形成两个新的region和两个新的HFile文件,一对一的进行管理,将原来旧的region和分裂之前大的HFile文件慢慢地就会下线处理。
- hbase 读流程
1)是由客户端发起读取数据的请求,首先会与zookeeper建立连接
2)从zookeeper中获取一个hbase:meta表位置信息,被哪一个regionserver所管理着
hbase:meta表:hbase的元数据表,在这个表中存储了自定义表相关的元数据,包括表名,表有哪些列簇,表有哪些reguion,每个region存储的位置,每个region被哪个regionserver所管理,这个表也是存储在某一个region上的,并且这个meta表只会被一个regionserver所管理。这个表的位置信息只有zookeeper知道。
3)连接这个meta表对应的regionserver,从meta表中获取当前你要读取的这个表对应的regionsever是谁。
当一个表多个region怎么办呢?
如果我们获取数据是以get的方式,只会返回一个regionserver
如果我们获取数据是以scan的方式,会将所有的region对应的regionserver的地址全部返回。
4)连接要读取表的对应的regionserver,从regionserver上的开始读取数据:
读取顺序:memstore–>blockcache–>storefile–>Hfile中
注意:如果是scan操作,就不仅仅去blockcache了,而是所有都会去找。
6.1.5 MapReduce为什么一定要有Shuffle过程
Shuffle 是 MapReduce 整个工作流程的核心环节。是指针对Map输出结果进行分区、排序和合并等处理,并交给Reduce的过程。
6.1.6 MapReduce中的三次排序
1)当map函数产生输出时,会首先写入内存的环形缓冲区,当达到设定的阀值,在刷写磁盘之前,后台线程会将缓冲区的数据划分成相应的分区。在每个分区中,后台线程按键进行内排序
2)在Map任务完成之前,磁盘上存在多个已经分好区,并排好序的,大小和缓冲区一样的溢写文件,这时溢写文件将被合并成一个已分区且已排序的输出文件。由于溢写文件已经经过第一次排序,所有合并文件只需要再做一次排序即可使输出文件整体有序。
3)在reduce阶段,需要将多个Map任务的输出文件copy到ReduceTask中后合并,由于经过第二次排序,所以合并文件时只需再做一次排序即可使输出文件整体有序
在这3次排序中第一次是内存缓冲区做的内排序,使用的算法是快速排序,第二次排序和第三次排序都是在文件合并阶段发生的,使用的是归并排序。
6.1.7 MapReduce为什么不能产生过多小文件
默认情况下,TextInputFormat对任务的切片机制是按照文件规划切片,不管有多少个小文件,都会是单独的切片,都会交给一个maptask,这样,如果有大量的小文件就会产生大量的maptask,处理效率低。
6.2 实战
python hadoop streaming 配置参数含义:
-D stream.map.output.field.separator :设置 map 输出中 key 和 value 的分隔符
-D stream.num.map.output.key.fields :设置 map 程序分隔符的位置,该位置之前的部分作为 key,之后的部分作为 value
-D map.output.key.field.separator : 设置 map 输出分区时 key 内部的分割符
-D mapreduce.partition.keypartitioner.options : 指定分桶时,key 按照分隔符切割后,其中用于分桶 key 所占的列数(配合 -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner 使用)
-D stream.reduce.output.field.separator:设置 reduce 输出中 key 和 value 的分隔符
-D stream.num.reduce.output.key.fields:设置 reduce 程序分隔符的位置
'mapreduce.partition.keypartitioner.options':'-k1,2' 表示设置按前两个字段进行分区
根据自己的理解,完成了方法1的部分代码:
from mrjob.job import MRJob
from mrjob.step import MRStep
class Project1(MRJob):
def mapper(self, _, line):
userID, locID, time = line.split(",")
#填入mapper的具体步骤
yield userID, 1
yield locID, 1
yield time, 1
def combiner(self, key, values):
#填入combiner的具体步骤
pass
def reducer_init(self):
# 填入reducer_init的具体步骤
pass
def reducer(self, key, values):
userID, locID = key.split(",")
# 填入reducer的具体步骤
yield key, sum(values)
def reducer_sort(self, key, _):
userID, locID, v = key.split("#")
yield locID, f'{userID},{v}'
SORT_VALUES = True
def steps(self):
#填入配置参数
JOBCONF1 = {
'mapreduce.map.output.key.field.separator': ",",
'mapreduce.partition.keypartitioner.options': '-k1,1 -k2,2',
# Below is not necessary, but you can still do it
# 'mapreduce.job.output.key.comparator.class': 'org.apache.hadoop.mapreduce.lib.partition.KeyFieldBasedComparator',
# 'mapreduce.partition.keycomparator.options': '-k1,1 -k2,2',
}
# 填入配置参数
JOBCONF2 = {
'mapreduce.map.output.key.field.separator': "#",
'mapreduce.partition.keypartitioner.options': '-k1,1',
'mapreduce.job.output.key.comparator.class': 'org.apache.hadoop.mapreduce.lib.partition.KeyFieldBasedComparator',
'mapreduce.partition.keycomparator.options': '-k1,1',
}
return [
MRStep(jobconf=JOBCONF1, mapper=self.mapper, combiner=self.combiner, reducer_init=self.reducer_init,
reducer=self.reducer),
MRStep(jobconf=JOBCONF2, reducer=self.reducer_sort)
]
if __name__ == '__main__':
Project1.run()