1. 下面哪个程序负责 HDFS 数据存储?
a)NameNode b)Jobtracker c)Datanode d)secondaryNameNode e)tasktracker
答案 C datanode
2. HDfS 中的 block 默认保存几份?
a)3 份 b)2 份 c)1 份 d)不确定
答案 A 默认 3 份
3. 下列哪个程序通常与NameNode在一个节点启动?
a)SecondaryNameNode b)DataNode c)TaskTracker d)Jobtracker
答案 D
4. HDFS 默认 Block Size
1.x是64M; 2.x是128M
5. 下列哪项通常是集群的最主要瓶颈?
a)CPU b)网络 c)磁盘 IO d)内存
答案:C 磁盘
首先集群的目的是为了节省成本,用廉价的 pc 机,取代小型机及大型机。小型机和大型机有什么特点?
1).cpu 处理能力强
2).内存够大,所以集群的瓶颈不可能是 a 和 d
3).如果是互联网有瓶颈,可以让集群搭建内网。每次写入数据都要通过网络(集群是内网),然后还要写入 3 份数据,所以 IO 就会打折扣。
6. 关于 SecondaryNameNode 哪项是正确的?
a)它是 NameNode 的热备
b)它对内存没有要求
c)它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间
d)SecondaryNameNode 应与 NameNode 部署到一个节点
答案 C。
7. 下列哪项可以作为集群的管理?
a)Puppet b)Pdsh c)Cloudera Manager d)Zookeeper
答案 ABD
具体可查看什么是 Zookeeper,Zookeeper 的作用是什么,在 Hadoop 及 hbase 中具体作用是什么。
8. Client 端上传文件的时候下列哪项正确?
a)数据经过 NameNode 传递给 DataNode
b)Client 端将文件切分为 Block,依次上传
c)Client 只上传数据到一台 DataNode,然后由 NameNode 负责 Block 复制工作
答案 B
分析:Client 向 NameNode 发起文件写入的请求。NameNode 根据文件大小和文件块配置情况,返回给 Client 它所管理部分 DataNode 的信息。Client 将文件划分为多个 Block,根据 DataNode 的地址信息,按顺序写入到每一个DataNode 块中。具体查看HDFS 体系结构简介及优缺点。
9. 下列哪个是 Hadoop 运行的模式
a)单机版 b)伪分布式 c)分布式
答案 ABC 单机版,伪分布式只是学习用的。
10. Hadoop的核心配置是什么?
Hadoop的核心配置通过两个xml文件来完成:1,hadoop-default.xml;2,hadoop-site.xml。这些文件都使用xml格式,因此每个xml中都有一些属性,包括名称和值,但是当下这些文件都已不复存在。
10.1 那当下又该如何配置?
Hadoop现在拥有3个配置文件:1,core-site.xml;2,hdfs-site.xml;3,mapred-site.xml。这些文件都保存在conf/子目录下。
11. hadoop中Combiner的作用?
combiner是reduce的实现,在map端运行计算任务,减少map端的输出数据。
combiner的意义就是对每一个maptask的输出进行局部汇总,以减小网络传输量
作用就是优化。
但是combiner的使用场景是: mapreduce的map和reduce两者的输入输出一样,否则不能使用Combiner。
12. 简述hadoop安装。
13. 请列出hadoop进程名
14. 解决下面的错误
- 权限问题,可能曾经用root启动过集群。(例如hadoop搭建的集群,是tmp/hadoop-hadoop/.....)
- 可能是文件夹不存在
- 解决: 删掉tmp下的那个文件,或改成当前用户
15. 简述hadoop的调度器
16. hive有哪些保存元数据的方式,个有什么特点。
- 内存数据库derby,安装小,但是数据存在内存,不稳定。
- mysql数据库,数据存储模式可以自己设置,持久化好,查看方便。
17. combiner和partition的作用
combiner是reduce的实现,在map端运行计算任务,减少map端的输出数据。作用就是优化。
但是combiner的使用场景是mapreduce的map输出结果和reduce输入输出一样。
partition的默认实现是hashpartition,是map端将数据按照reduce个数取余,进行分区,不同的reduce来copy自己的数据。
partition的作用是将数据分到不同的reduce进行计算,加快计算速度。
18. hive内部表和外部表的区别
内部表:加载数据到hive所在的hdfs目录,删除时,元数据和数据文件都删除
外部表:不加载数据到hive所在的hdfs目录,删除时,只删除表结构。
19. hbase的rowkey怎么创建好?列族怎么创建比较好?
hbase存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)
一个列族在数据底层是一个文件,所以将经常一起查询的列放到一个列族中,列族要尽量少,减少文件的寻址时间。
20. 用mapreduce怎么处理数据倾斜问题?
数据倾斜:map /reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长,这是因为某一个key的条数比其他key多很多(有时是百倍或者千倍之多),这条key所在的reduce节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完,此称之为数据倾斜。
用hadoop程序进行数据关联时,常碰到数据倾斜的情况,这里提供一种解决方法。
自己实现partition类,用key和value相加取hash值:
方式1:
源代码:
public int getPartition(K key, V value,
int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
修改后
public int getPartition(K key, V value,
int numReduceTasks) {
return (((key).hashCode()+value.hashCode()) & Integer.MAX_VALUE) % numReduceTasks;
}
方式2:
public class HashPartitioner<K, V> extends Partitioner<K, V> {
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;
}
21. 我们开发job时,是否可以去掉reduce阶段。
可以。设置reduce数为0 即可。
22. datanode在什么情况下不会备份
datanode在强制关闭或者非常断电不会备份。
23. combiner出现在那个过程
出现在map阶段的map方法后。
24. hdfs的体系结构
hdfs有namenode、secondraynamenode、datanode组成。
为n+1模式
namenode负责管理datanode和记录元数据
secondraynamenode负责合并日志
datanode负责存储数据
25. 3个datanode中有一个datanode出现错误会怎样?
这个datanode的数据会在其他的datanode上重新做备份。
26. 描述一下hadoop中,有哪些地方使用了缓存机制,作用分别是什么?
在mapreduce提交job的获取id之后,会将所有文件存储到分布式缓存上,这样文件可以被所有的mapreduce共享。
27. 如何确定hadoop集群的健康状态
通过页面监控,脚本监控(监控相关进程是否存活)。
28. 生产环境中为什么建议使用外部表?
1) 因为外部表不会加载数据到hive,减少数据传输、数据还能共享。
2) hive不会修改数据,所以无需担心数据的损坏
3) 删除表时,只删除表结构、不删除数据。
29. 如何检查namenode是否正常运行?重启namenode的命令是什么?
通过节点信息和浏览器查看,通过脚本监控
如何namenode还在运行,则
hadoop-daemon.sh stop namenode
hadoop-daemon.sh start namenode
否则:
hadoop-daemon.sh start namenode
30. 避免namenode故障导致集群拓机的方法是什么?
HA高可用
31. hbase数据库对行键的设计要求是什么?
行健以字典序排列,设计时充分利用这个特点,将经常一起查询的行健设计在一起,例如时间戳结尾,用户名开头(位置相关性)
32. 一个datanode 宕机,怎么一个流程恢复
datanode 宕机后,其数据会重新备份到其它可用的datanode上,因此将datanode数据删除,重新当成新节点加入即可。
33. Hbase 的特性,以及你怎么去设计 rowkey 和 columnFamily ,怎么去建一个table
hbase是列式数据库,rowkey是字典序的,设计时的规则同上。
每个列族是一个文件,将经常一起查询的列放到同一个列族中,减少文件的寻址时间。
34. Mapreduce 的 map 数量 和 reduce 数量 怎么确定 ,怎么配置
map的数量有数据块决定,reduce数量可以通过参数配置。
35. 给定a,b两个文件,每个文件中有50亿个url,每个url为64字节,内存限制为4G,怎样找出两个文件中共同的url?
可以估计每个文件的大小为50亿×64=298G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
- 将文件存储到hdfs中,这样每个文件为64M或者是128M
- 分别对两个文件的url进行去重、排序输出,这样能排除a文件中相同的url,b文件也一样
- 对a、b两个文件处理后的结果进行wordcount,并且在reduce中判断单词个数,个数为2的时候输出,这样就找到了a、b文件中的相同url。
此计算步骤中的每一步加载到内存中的文件大小都不会超过64M,远远小于4G。
36. 1亿个数据获取前100个最大值
topk,强调使用treemap是为了节省内存计算空间。
37. 多线程实现方式 Thread和Runnable的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
下面看例子:
- package org.thread.demo;
- class MyThread extends Thread{
- private String name;
- public MyThread(String name) {
- super();
- this.name = name;
- }
- public void run(){
- for(int i=0;i<10;i++){
- System.out.println("线程开始:"+this.name+",i="+i);
- }
- }
- }
- package org.thread.demo;
- public class ThreadDemo01 {
- public static void main(String[] args) {
- MyThread mt1=new MyThread("线程a");
- MyThread mt2=new MyThread("线程b");
- mt1.run();
- mt2.run();
- }
- }
但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。下面启动start()方法启动线程:
- package org.thread.demo;
- public class ThreadDemo01 {
- public static void main(String[] args) {
- MyThread mt1=new MyThread("线程a");
- MyThread mt2=new MyThread("线程b");
- mt1.start();
- mt2.start();
- }
- };
这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?
在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,可以发现此方法中使用了private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)
Runnable接口
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。
- public interface Runnable{
- public void run();
- }
例子:
- package org.runnable.demo;
- class MyThread implements Runnable{
- private String name;
- public MyThread(String name) {
- this.name = name;
- }
- public void run(){
- for(int i=0;i<100;i++){
- System.out.println("线程开始:"+this.name+",i="+i);
- }
- }
- };
但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):
- package org.runnable.demo;
- import org.runnable.demo.MyThread;
- public class ThreadDemo01 {
- public static void main(String[] args) {
- MyThread mt1=new MyThread("线程a");
- MyThread mt2=new MyThread("线程b");
- new Thread(mt1).start();
- new Thread(mt2).start();
- }
- }
两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
- 避免点继承的局限,一个类可以继承多个接口。
- 适合于资源的共享
以卖票程序为例,通过Thread类完成:
- package org.demo.dff;
- class MyThread extends Thread{
- private int ticket=10;
- public void run(){
- for(int i=0;i<20;i++){
- if(this.ticket>0){
- System.out.println("卖票:ticket"+this.ticket--);
- }
- }
- }
- };
下面通过三个线程对象,同时卖票:
- package org.demo.dff;
- public class ThreadTicket {
- public static void main(String[] args) {
- MyThread mt1=new MyThread();
- MyThread mt2=new MyThread();
- MyThread mt3=new MyThread();
- mt1.start();//每个线程都各卖了10张,共卖了30张票
- mt2.start();//但实际只有10张票,每个线程都卖自己的票
- mt3.start();//没有达到资源共享
- }
- }
如果用Runnable就可以实现资源共享,下面看例子:
- package org.demo.runnable;
- class MyThread implements Runnable{
- private int ticket=10;
- public void run(){
- for(int i=0;i<20;i++){
- if(this.ticket>0){
- System.out.println("卖票:ticket"+this.ticket--);
- }
- }
- }
- }
- package org.demo.runnable;
- public class RunnableTicket {
- public static void main(String[] args) {
- MyThread mt=new MyThread();
- new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一
- new Thread(mt).start();//个实例化对象mt,就会出现异常
- new Thread(mt).start();
- }
- };
虽然现在程序中有三个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。
38. 一个HADOOP环境整合了HBASE和HIVE,是否有必要给HDFS和HBASE都分别配置压缩策略?请给出压缩策略建议。
hdfs在存储的时候不会将数据进行压缩,如果想进行压缩,我们可以在向hdfs上传数据的时候进行压缩。
1) 采用压缩流
//压缩文件 public static void compress(String codecClassName) throws Exception{ Class<?> codecClass = Class.forName(codecClassName); Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(conf); CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, conf); //指定压缩文件路径 FSDataOutputStream outputStream = fs.create(new Path("/user/hadoop/text.gz")); //指定要被压缩的文件路径 FSDataInputStream in = fs.open(new Path("/user/hadoop/aa.txt")); //创建压缩输出流 CompressionOutputStream out = codec.createOutputStream(outputStream); IOUtils.copyBytes(in, out, conf); IOUtils.closeStream(in); IOUtils.closeStream(out); } |
2)采用序列化文件
public void testSeqWrite() throws Exception {
Configuration conf = new Configuration();// 创建配置信息 conf.set("fs.default.name", "hdfs://master:9000");// hdfs默认路径 conf.set("hadoop.job.ugi", "hadoop,hadoop");// 用户和组信息 String uriin = "hdfs://master:9000/ceshi2/";// 文件路径 FileSystem fs = FileSystem.get(URI.create(uriin), conf);// 创建filesystem Path path = new Path("hdfs://master:9000/ceshi3/test.seq");// 文件名 IntWritable k = new IntWritable();// key,相当于int Text v = new Text();// value,相当于String SequenceFile.Writer w = SequenceFile.createWriter(fs, conf, path, k.getClass(), v.getClass());// 创建writer for (int i = 1; i < 100; i++) {// 循环添加 k.set(i); v.set("abcd"); w.append(k, v); } w.close(); IOUtils.closeStream(w);// 关闭的时候flush fs.close(); } |
hbase为列存数据库,本身存在压缩机制,所以无需设计。
39. 如果要存储海量小文件,请简述方案。
1)将小文件打成har文件存储
2)将小文件合并成大文件(用数据库存储每个小文件的起始位置)
3)将小文件序列化到hdfs中
40. 说下对hadoop 的一些理解,包括哪些组件
详谈hadoop的应用,包括的组件分为三类,分别说明hdfs,yarn,mapreduce
41. Kafka direct API
为了WAL的性能损失和exactly-once,spark streaming1.3中使用Kafka direct API。非常巧妙,Spark driver计算下个batch的offsets,指导executor消费对应的topics和partitions。消费Kafka消息,就像消费文件系统文件一样。
- 不再需要kafka receivers,executor直接通过Kafka API消费数据
- WAL不再需要,如果从失败恢复,可以重新消费
- exactly-once得到了保证,不会再从WAL中重复读取数据
42. 假如一个分区的数据主部错误怎么通过hivesql删除hdfs
alter table ptable drop partition (daytime='20140911',city='bj');
元数据,数据文件都删除,但目录daytime= 20140911还在
43. 请写出以下的shell命令
(1)杀死一个job
(2)删除hdfs上的 /tmp/aaa目录
(3)加入一个新的存储节点和删除一个节点需要执行的命令
Answers:
(1)hadoop job –list 得到job的id,然后执 行 hadoop job -kill jobId就可以杀死一个指定jobId的job工作了。
yarn application -kill application_1513303851617_0784 ---yarn集群杀死job方式
(2)hadoop fs -rmr /tmp/aaa
(3) 增加一个新的节点在新的几点上执行
Hadoop daemon.sh start datanode
Hadooop daemon.sh start tasktracker/nodemanager
43. 简述hadoop实现join的几种方法
reduce side join
reduce side join是一种最简单的join方式,其主要思想如下:
在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源的key/value数据对,对每条数据打一个标签 (tag),比如:tag=0表示来自文件File1,tag=2表示来自文件File2。即:map阶段的主要任务是对不同文件中的数据打标签。
在reduce阶段,reduce函数获取key相同的来自File1和File2文件的value list, 然后对于同一个key,对File1和File2中的数据进行join(笛卡尔乘积)。即:reduce阶段进行实际的连接操作。
map side join
之所以存在reduce side join,是因为在map阶段不能获取所有需要的join字段,即:同一个key对应的字段可能位于不同map中。Reduce side join是非常低效的,因为shuffle阶段要进行大量的数据传输。
Map side join是针对以下场景进行的优化:两个待连接表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多 份,让每个map task内存中存在一份(比如存放到hash table中),然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否有相同的key的记录,如果有,则连接后输出即可。
SemiJoin
SemiJoin,也叫半连接,是从分布式数据库中借鉴过来的方法。它的产生动机是:对于reduce side join,跨机器的数据传输量非常大,这成了join操作的一个瓶颈,如果能够在map端过滤掉不会参加join操作的数据,则可以大大节省网络IO。
实现方法很简单:选取一个小表,假设是File1,将其参与join的key抽取出来,保存到文件File3中,File3文件一般很小,可以放到 内存中。在map阶段,使用DistributedCache将File3复制到各个TaskTracker上,然后将File2中不在File3中的 key对应的记录过滤掉,剩下的reduce阶段的工作与reduce side join相同。
44. 请简述mapreduce中的combine和partition的作用
combiner是发生在map的最后一个阶段,其原理也是一个小型的reducer,主要作用是减少输出到reduce的数据量,缓解网络传输瓶颈,提高reducer的执行效率。
partition的主要作用将map阶段产生的所有kv对分配给不同的reducer task处理,可以将reduce阶段的处理负载进行分摊
45. datanode在什么情况下不会备份数据
在客户端上传文件时指定文件副本数量为1
46. combine出现在哪个过程
shuffle过程中
具体来说,是在maptask输出的数据从内存溢出到磁盘,可能会调多次
Combiner使用时候要特别谨慎,不能影响最后的逻辑结果
47. 三个datanode中当有一个datanode出现错误时会怎样?
Namenode会通过心跳机制感知到datanode下线
会将这个datanode上的block块在集群中重新复制一份,恢复文件的副本数量
会引发运维团队快速响应,派出同事对下线datanode进行检测和修复,然后重新上线
48. MapReduce优化经验
- 设置合理的map和reduce的个数。合理设置blocksize
- 避免出现数据倾斜
- combine函数
- 对数据进行压缩
- 小文件处理优化:事先合并成大文件,combineTextInputformat,在hdfs上用mapreduce将小文件合并成SequenceFile大文件(key:文件名,value:文件内容)
- 参数优化
49. java 是传值还是传址?
引用传递。
50. 请简述hadoop怎样实现二级排序
第一种方法是,Reducer将给定key的所有值都缓存起来,然后对它们再做一个Reducer内排序。但是,由于Reducer需要保存给定key的所有值,可能会导致出现内存耗尽的错误。
第二种方法是,将值的一部分或整个值加入原始key,生成一个合成key。这两种方法各有优势,第一种方法可能会更快一些(但有内存耗尽的危险),第二种方法则是将排序的任务交给MapReduce框架,更符合Hadoop/Reduce的设计思想。这篇文章里选择的是第二种。我们将编写一个Partitioner,确保拥有相同key(原始key,不包括添加的部分)的所有数据被发往同一个Reducer,还将编写一个Comparator,以便数据到达Reducer后即按原始key分组。
二次排序
有两种方法进行二次排序,分别为:buffer and in memory sort和 value-to-key conversion。
对于buffer and in memory sort,主要思想是:在reduce()函数中,将某个key对应的所有value保存下来,然后进行排序。 这种方法最大的缺点是:可能会造成out of memory。
对于value-to-key conversion,主要思想是:将key和部分value拼接成一个组合key(实现WritableComparable接口或者调用 setSortComparatorClass函数),这样reduce获取的结果便是先按key排序,后按value排序的结果,需要注意的是,用户需 要自己实现Paritioner,以便只按照key进行数据划分。Hadoop显式的支持二次排序,在Configuration类中有个 setGroupingComparatorClass()方法,可用于设置排序group的key值
51. hive中如何定义udf
- 继承UDF类
- 重写evaluate方法
- 打jar包
- 上传jar包
hive> add jar /home/hadoop/lib/hive-1.0.jar
- 创建函数
create temporary function my_lower as 'com.example.hive.udf.Lower';
create function my_db.my_lower as 'com.example.hive.udf.Lower';
- 使用