大数据面试题汇总【有自己的和网上总结的】

1. 选择题

1.1. 下面哪个程序负责 HDFS 数据存储。

c)Datanode 
答案 C datanode

1.2. HDfS 中的 block 默认保存几份?

a)3 份 
答案 A 默认 3 份

1.3. 下列哪个程序通常与 NameNode 在一个节点启动?

d)Jobtracker 
答案 D

1.4. HDFS 默认 Block Size

c)128MB 
答案:C

1.5. 下列哪项通常是集群的最主要瓶颈

c)磁盘 IO 
答案:C 磁盘 
首先集群的目的是为了节省成本,用廉价的 pc 机,取代小型机及大型机。小型机和大型机有什么特点? 
1.cpu 处理能力强 
2.内存够大,所以集群的瓶颈不可能是 a 和 d 
3.如果是互联网有瓶颈,可以让集群搭建内网。每次写入数据都要通过网络(集群是内网),然后还要写入 3 份数据,所以 IO 就会打折扣。

1.6. 关于 SecondaryNameNode 哪项是正确的?

c)它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间 
答案 C。

1.7. 下列哪项可以作为集群的管理?

a)Puppet b)Pdsh d)Zookeeper 
答案 ABD 
具体可查看什么是 Zookeeper,Zookeeper 的作用是什么,在 Hadoop 及 hbase 中具体作用是什么。

1.8. Client 端上传文件的时候下列哪项正确

b)Client 端将文件切分为 Block,依次上传 
答案 B 
分析:Client 向 NameNode 发起文件写入的请求。NameNode 根据文件大小和文件块配置情况,返回给 Client 它所管理部分 DataNode 的信息。 
Client 将文件划分为多个 Block,根据 DataNode 的地址信息,按顺序写入到每一个DataNode 块中。具体查看HDFS 体系结构简介及优缺点。

1.9. 下列哪个是 Hadoop 运行的模式

a)单机版 b)伪分布式 c)分布式 
答案 ABC 单机版,伪分布式只是学习用的。

2. 面试题

2.1. Hadoop的核心配置是什么?

Hadoop的核心配置通过两个xml文件来完成:1,hadoop-default.xml;2,hadoop-site.xml。这些文件都使用xml格式,因此每个xml中都有一些属性, 
包括名称和值,但是当下这些文件都已不复存在。

2.2. 那当下又该如何配置?

Hadoop现在拥有4个配置文件:1,core-site.xml;2,hdfs-site.xml;3,mapred-site.xml;4,yarn-site.xml。这些文件都保存在conf/子目录下。

2.3. “jps”命令的用处?

这个命令可以检查Namenode、Datanode、Task Tracker、 Job Tracker是否正常工作。

2.4. mapreduce的原理?

MapReduce采用”分而治之”的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成,然后通过整合各个节点的中间结果, 
得到最终结果。简单地说,MapReduce就是”任务的分解与结果的汇总”。 
在Hadoop中,用于执行MapReduce任务的机器角色有两个:一个是JobTracker;另一个是TaskTracker,JobTracker是用于调度工作的,TaskTracker 
是用于执行工作的。一个Hadoop集群中只有一台JobTracker。 
在分布式计算中,MapReduce框架负责处理了并行编程中分布式存储、工作调度、负载均衡、容错均衡、容错处理以及网络通信等复杂问题,把处理 
过程高度抽象为两个函数:map和reduce,map负责把任务分解成多个任务,reduce负责把分解后多任务处理的结果汇总起来。 
需要注意的是,用MapReduce来处理的数据集(或任务)必须具备这样的特点:待处理的数据集可以分解成许多小的数据集,而且每一个小数据集都 
可以完全并行地进行处理。

2.5. HDFS存储的机制?

2.5.1. hdfs写流程

流程: 
client链接namenode存数据 
namenode记录一条数据位置信息(元数据),告诉client存哪。 
client用hdfs的api将数据块(默认是64M)存储到datanode上。 
datanode将数据水平备份。并且备份完将反馈client。 
client通知namenode存储块完毕。 
namenode将元数据同步到内存中。 
另一块循环上面的过程。

2.5.2. 读流程

流程: 
client链接namenode,查看元数据,找到数据的存储位置。 
client通过hdfs的api并发读取数据。 
关闭连接。

2.6. 举一个简单的例子说明mapreduce是怎么来运行的 ?

  MapReduce运行的时候,会通过Mapper运行的任务读取HDFS中的数据文件,然后调用自己的方法,处理数据,最后输出。 
  Reducer任务会接收Mapper任务输出的数据,作为自己的输入数据,调用自己的方法,最后输出到HDFS的文件中。 
Mapper任务的执行过程详解 
  每个Mapper任务是一个Java进程,它会读取HDFS中的文件,解析成很多的键值对,经过我们覆盖的map方法处理后, 
转换为很多的键值对再输出。整个Mapper任务的处理过程又可以分为以下六个阶段: 
  第一阶段是把输入文件按照一定的标准分片(InputSplit),每个输入片的大小是固定的。默认情况下,输入片(InputSplit) 
的大小与数据块(Block)的大小是相同的。如果数据块(Block)的大小是默认值128MB,输入文件有两个,一个是32MB,一个是 172MB。那么小的文件是一个输入片,大文件会分为两个数据块,那么是两个输入片。一共产生三个输入片。每一个输入片由 一个Mapper进程处理。这里的三个输入片,会有三个Mapper进程处理。

  第二阶段是对输入片中的记录按照一定的规则解析成键值对。有个默认规则是把每一行文本内容解析成键值对。“键”是每一 行的起始位置(单位是字节),“值”是本行的文本内容。 
   
  第三阶段是调用Mapper类中的map方法。第二阶段中解析出来的每一个键值对,调用一次map方法。如果有1000个键值对,就会 调用1000次map方法。每一次调用map方法会输出零个或者多个键值对。

  第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。比较是基于键进行的。比如我们的键表示省份(如北京、上海、 山东等),那么就可以按照不同省份进行分区,同一个省份的键值对划分到一个区中。默认是只有一个区。分区的数量就是Reducer 任务运行的数量。默认只有一个Reducer任务。 
第五阶段是对每个分区中的键值对进行排序。首先,按照键进行排序,对于键相同的键值对,按照值进行排序。比如三个键值 对<2,2>、<1,3>、<2,1>,键和值分别是整数。那么排序后的结果是<1,3>、<2,1>、<2,2>。如果有第六阶段,那么进入

第六阶段 如果没有,直接输出到本地的Linux文件中。 第六阶段是对数据进行归约处理,也就是reduce处理。键相等的键值对会调用一次reduce方法。经过这一阶段,数据量会减少。 归约后的数据输出到本地的linxu文件中。本阶段默认是没有的,需要用户自己增加这一阶段的代码。 Reducer任务的执行过程详解 
每个Reducer任务是一个java进程。Reducer任务接收Mapper任务的输出,归约处理后写入到HDFS中,可以分为三个阶段: 
第一阶段是Reducer任务会主动从Mapper任务复制其输出的键值对。Mapper任务可能会有很多,因此Reducer会复制多个Mapper的输出。 
第二阶段是把复制到Reducer本地数据,全部进行合并,即把分散的数据合并成一个大的数据。再对合并后的数据排序。 
第三阶段是对排序后的键值对调用reduce方法。键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对。 
最后把这些输出的键值对写入到HDFS文件中。 
在整个MapReduce程序的开发过程中,我们最大的工作量是覆盖map函数和覆盖reduce函数。

 

 

2.7. 用mapreduce来实现下面需求? 
现在有10个文件夹,每个文件夹都有1000000个url.现在让你找出top1000000url。 
方法一: 
运用2个job,第一个job直接用filesystem读取10个文件夹作为map输入,url做key,reduce计算url的sum, 
下一个job map用url作key,运用sum作二次排序,reduce中取top10000000 
1:首先进行wordcount计算 
2:进行二次排序 
如何启动两个job代码如下: 
public class TopUrl { 
public static void main(String[] args) throws Exception { 
depending(); 
}

public static void depending() throws Exception{ 
Configuration conf = new Configuration(); 
//排序 
Job job2 = initJob2(conf, SecondSortMapper.class,SecondSortReduce.class); 
//读取url 
Job job1 = initJob(conf, FirstMapper.class, FirstReduce.class);

  JobControl jobControl = new JobControl("groupName");
  List  dependingJobs = new ArrayList();
  //进行排序
  ControlledJob controlledJob1 = new ControlledJob(conf);
  controlledJob1.setJob(job1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

// dependingJobs.add(controlledJob1);

  //排序
  ControlledJob controlledJob2 = new ControlledJob(conf);
  controlledJob2.setJob(job2);
  controlledJob2.addDependingJob(controlledJob1);
  jobControl.addJob(controlledJob2);
  jobControl.addJob(controlledJob1);


Thread jcThread = new Thread(jobControl);
jcThread.start();
while(true){
if(jobControl.allFinished()){
     System.out.println(jobControl.getSuccessfulJobList());
     jobControl.stop();
     break;
}
if(jobControl.getFailedJobList().size() > 0){
     System.out.println(jobControl.getFailedJobList());
     jobControl.stop();
     break;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

}

FileSystem fs = FileSystem.get(conf); 
boolean ret = fs.deleteOnExit(new Path(“hdfs://master:9000/user/hadoop/20130601/output”)); 
System.out.println(ret); 
}

public static Job initJob2(Configuration conf,Class o1,Class o2) throws Exception{ 
Job job = new Job(conf, “Join2”); 
job.setJarByClass(TopUrl.class); 
job.setMapperClass(o1); 
job.setMapOutputKeyClass(Text.class);//map输出key 
job.setOutputValueClass(IntWritable.class); 
job.setReducerClass(o2); 
job.setOutputKeyClass(Text.class); //reduce输出key 
job.setOutputValueClass(IntWritable.class);

// job.setPartitionerClass(cls); 
job.setSortComparatorClass(TextIntComparator.class); 
// job.setGroupingComparatorClass(cls);

  FileInputFormat.addInputPath(job, new Path
  ("hdfs://master:9000/user/hadoop/20130601/output"));
  FileOutputFormat.setOutputPath(job, new Path
  ("hdfs://master:9000/user/hadoop/20130601/output2"));
  • 1
  • 2
  • 3
  • 4
  • 5

// System.exit(job.waitForCompletion(true) ? 0 : 1); 
return job; 
}

public static Job initJob(Configuration conf,Class o1,Class o2) throws Exception{ 
Job job = new Job(conf, “Join1”); 
job.setJarByClass(TopUrl.class); 
job.setMapperClass(o1); 
job.setMapOutputKeyClass(Text.class);//map输出key 
job.setOutputValueClass(IntWritable.class); 
job.setReducerClass(o2); 
job.setOutputKeyClass(Text.class); //reduce输出key 
job.setOutputValueClass(IntWritable.class); 
FileInputFormat.addInputPath(job, new Path 
(“hdfs://master:9000/user/hadoop/20130601/ippaixv.txt”)); 
FileOutputFormat.setOutputPath(job, new Path 
(“hdfs://master:9000/user/hadoop/20130601/output”)); 
// System.exit(job.waitForCompletion(true) ? 0 : 1); 
return job; 

}

方法二: 
建hive表A,挂分区channel,每个文件夹是一个分区. 
select x.url,x.c from(select url,count(1) as c from A where channel =” group by url) x order by x.c desc limit 1000000; 
还可以用treeMap, 到1000000了每来一个都加进去, 删掉最小的

2.8. hadoop中Combiner的作用? 
combiner是reduce的实现,在map端运行计算任务,减少map端的输出数据。 
作用就是优化。 
但是combiner的使用场景是mapreduce的map和reduce输入输出一样。 
2.9. 简述hadoop安装 
1)创建hadoop用户 
2)改IP,修改HOSTS文件域名 
3)安装SSH,配置无密钥通信 
4)安装JAVA,配置JAVA的环境变量 
5)解压hadoop 
6)配置conf下的core-site.xml,hdfs-site.xml,mapred-site.xml,yarn-site.xml 
7)配置hadoop的环境变量 
8)hadoop namenode -format 
9)start-all.sh 
2.10. 请列出hadoop进程名 
Namenode:管理集群,并记录datanode文件信息。 
Secondarynamenode:可以做冷备,对一定范围内数据做快照性备份。 
Datanode:存储数据。 
Resourcemanager:管理任务,并将任务分配给MRAppMaster。 
NodeManager:任务执行方。 
2.11. 解决下面的错误 
1、权限问题,可能曾经用root启动过集群。(例如hadoop搭建的集群,是tmp/hadoop-hadoop/…..) 
2、可能是文件夹不存在 
3、解决: 删掉tmp下的那个文件,或改成当前用户 
2.12. 写出下面的命令 
1)杀死一个job 
hadoop job -list 拿到job-id, 
hadoop job -kill job-id 
2)删除hdfs上的/tmp/aaa目录 
hadoop fs -rmr /tmp/aaa 
3)加入一个新的存储节点和删除一个计算节点需要刷新集群状态命令 
加薪节点时: 
hadoop-daemon.sh start datanode 
yarn-daemon.sh start nodemanager 
删除时: 
hadoop mradmin -refreshnodes 
hadoop dfsadmin -refreshnodes

 

 

2.13. 简述hadoop的调度器 
FIFO schedular:默认,先进先出的原则 
Capacity schedular:计算能力调度器,选择占用最小、优先级高的先执行,依此类推 
Fair schedular:公平调度,所有的job具有相同的资源。 
2.14. 列出你开发mapreduce的语言 
java 
2.15. 书写程序 
wordcount 
mapper: 
String vStr = value.toString().split(“,”)[3]; 
ctx.write(new Text(vStr),new IntWriteble(1)); 
reducer : 
for(IntWriteble v : value){ 
ctx.write(key,v); 

2.16. 不同语言的优缺点 
你认为用Java,Streaming,pipe方式开发map/reduce,各有哪些优缺点。 
hadoop是java写的,java的集成效果最好,并且平台环境统一。 
2.17. hive有哪些保存元数据的方式,各有什么特点。 
内存数据库derby,安装小,但是数据存在内存,不稳定 
mysql数据库,数据存储模式可以自己设置,持久化好,查看方便。 
2.18. combiner和partition的作用 
combiner是reduce的实现,在map端运行计算任务,减少map端的输出数据。作用就是优化。但是combiner的使用场景是mapreduce的map 
输出结果和reduce输入输出一样。 
partition的默认实现是hashpartition,是map端将数据按照reduce个数取余,进行分区,不同的reduce来copy自己的数据。partition 
的作用是将数据分到不同的reduce进行计算,加快计算效果。 
2.19. hive内部表和外部表的区别 
内部表:加载数据到hive所在的hdfs目录,删除时,元数据和数据文件都删除 
外部表:不加载数据到hive所在的hdfs目录,删除时,只删除表结构。 
2.20. hbase的rowkey怎么创建好?列族怎么创建比较好? 
hbase存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。 
(位置相关性) 
一个列族在数据底层是一个文件,所以将经常一起查询的列放到一个列族中,列族尽量少,减少文件的寻址时间。 
2.21. 用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

/etc/init.d/cups stop

chkconfig cups off

关闭ipv6

vim /etc/modprobe.conf

添加内容 
alias net-pf-10 off 
alias ipv6 off 
调整文件最大打开数 
查看: ulimit -a 结果:open files (-n) 1024 
临时修改: ulimit -n 4096 
持久修改: 
vi /etc/security/limits.conf在文件最后加上: 
* soft nofile 65535 
* hard nofile 65535 
* soft nproc 65535 
* hard nproc 65535 
修改linux内核参数 
vi /etc/sysctl.conf 
添加 
net.core.somaxconn = 32768

web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,

所以有必要调整这个值。 
调整swap分区什么时候使用: 
查看:cat /proc/sys/vm/swappiness 
设置:vi /etc/sysctl.conf 
在这个文档的最后加上这样一行: vm.swappiness=10 
表示物理内存使用到90%(100-10=90)的时候才使用swap交换区 
关闭noatime 
vi /etc/fstab 
/dev/sda2 /data ext3 noatime,nodiratime 0 0 
设置readahead buffer 
blockdev –setra READAHEAD 512 /dev/sda 
以下是修改mapred-site.xml文件 
修改最大槽位数 
槽位数是在各个tasktracker上的mapred-site.xml上设置的,默认都是2 

mapred.tasktracker.map.tasks.maximum 




mapred.tasktracker.reduce.tasks.maximum 



调整心跳间隔 
集群规模小于300时,心跳间隔为300毫秒 
mapreduce.jobtracker.heartbeat.interval.min 心跳时间 
mapred.heartbeats.in.second 集群每增加多少节点,时间增加下面的值 
mapreduce.jobtracker.heartbeat.scaling.factor 集群每增加上面的个数,心跳增多少 
启动带外心跳 
mapreduce.tasktracker.outofband.heartbeat 默认是false 
配置多块磁盘 
mapreduce.local.dir 
配置RPC hander数目 
mapred.job.tracker.handler.count 默认是10,可以改成50,根据机器的能力 
配置HTTP线程数目 
tasktracker.http.threads 默认是40,可以改成100 根据机器的能力 
选择合适的压缩方式 
以snappy为例: 

mapred.compress.map.output 
true 


mapred.map.output.compression.codec 
org.apache.hadoop.io.compress.SnappyCodec 

启用推测执行机制 
推测执行(Speculative Execution)是指在分布式集群环境下,因为程序BUG,负载不均衡或者资源分布不均等原因,造成同一个job的多个 
task运行速度不一致,有的task运行速度明显慢于其他task(比如:一个job的某个task进度只有10%,而其他所有task已经运行完毕), 
则这些task拖慢了作业的整体执行进度,为了避免这种情况发生,Hadoop会为该task启动备份任务,让该speculative task与原始task同时 
处理一份数据,哪个先运行完,则将谁的结果作为最终结果。 
推测执行优化机制采用了典型的以空间换时间的优化策略,它同时启动多个相同task(备份任务)处理相同的数据块,哪个完成的早,则采用哪个task的结果,这样可防止拖后腿Task任务出现,进而提高作业计算速度,但是,这样却会占用更多的资源,在集群资源紧缺的情况下,设计合理的推测执行机制可在多用少量资源情况下,减少大作业的计算时间。 
mapred.map.tasks.speculative.execution 默认是true 
mapred.rduce.tasks.speculative.execution 默认是true 
设置是失败容忍度 
mapred.max.map.failures.percent 作业允许失败的map最大比例 默认值0,即0% 
mapred.max.reduce.failures.percent 作业允许失败的reduce最大比例 默认值0,即0% 
mapred.map.max.attemps 失败后最多重新尝试的次数 默认是4 
mapred.reduce.max.attemps 失败后最多重新尝试的次数 默认是4 
启动jvm重用功能 
mapred.job.reuse.jvm.num.tasks 默认值1,表示只能启动一个task,若为-1,表示可以最多运行数不限制 
设置任务超时时间 
mapred.task.timeout 默认值600000毫秒,也就是10分钟。 
合理的控制reduce的启动时间 
mapred.reduce.slowstart.completed.maps 默认值0.05 表示map任务完成5%时,开始启动reduce任务 
跳过坏记录 
当任务失败次数达到该值时,才会进入skip mode,即启用跳过坏记录数功能,也就是先试几次,不行就跳过 
mapred.skip.attempts.to.start.skipping 默认值 2 
map最多允许跳过的记录数 
mapred.skip.map.max.skip.records 默认值0,为不启用 
reduce最多允许跳过的记录数 
mapred.skip.reduce.max.skip.records 默认值0,为不启用 
换记录存放的目录 
mapred.skip.out.dir 默认值${mapred.output.dir}/_logs/

 

 

2.23. 我们开发job时,是否可以去掉reduce阶段。 
可以。设置reduce数为0 即可。 
2.24. datanode在什么情况下不会备份 
datanode在强制关闭或者非正常断电不会备份。 
2.25. combiner出现在那个过程 
出现在map阶段的map方法后等。 
2.26. hdfs的体系结构 
hdfs有namenode、secondraynamenode、datanode组成。 
为n+1模式 
namenode负责管理datanode和记录元数据 
secondraynamenode负责合并日志 
datanode负责存储数据 
2.27. 3个datanode中有一个datanode出现错误会怎样? 
这个datanode的数据会在其他的datanode上重新做备份。 
2.28. 描述一下hadoop中,有哪些地方使用了缓存机制,作用分别是什么? 
在mapreduce提交job的获取id之后,会将所有文件存储到分布式缓存上,这样文件可以被所有的mapreduce共享。 
2.29. 如何确定hadoop集群的健康状态 
通过页面监控,脚本监控。 
2.30.生产环境中为什么建议使用外部表? 
1、因为外部表不会加载数据到hive,减少数据传输、数据还能共享。 
2、hive不会修改数据,所以无需担心数据的损坏 
3、删除表时,只删除表结构、不删除数据。 
3.15期新增 
3.1.新增 
1)hadoop的核心配置文件名称是什么? 
core-site.xml 
2)“jps”命令的用处? 
jps位于jdk的bin目录下,其作用是显示当前系统的java进程情况,及其id号。 jps相当于Solaris进程工具ps。不象”pgrep java” 
或”ps -ef grep java”,jps并不使用应用程序名来查找JVM实例。因此,它查找所有的Java应用程序,包括即使没有使用java执行 
体的那种(例如,定制的启动 器)。另外,jps仅查找当前用户的Java进程,而不是当前系统中的所有进程。 
3)如何检查namenode是否正常运行?重启namenode的命令是什么? 
通过节点信息和浏览器查看,通过脚本监控 
hadoop-daemon.sh start namenode 
hdfs-daemon.sh start namenode 
4)避免namenode故障导致集群宕机的解决方法是什么? 
自己书写脚本监控重启 
5)hbase数据库对行键的设计要求是什么? 
行健以字典序排列,设计时充分利用这个特点,将经常一起查询的行健设计在一起,例如时间戳结尾,用户名开头(位置相关性) 
6)请给出你的设计方案,比如使用哪些技术框架,该框架起到的作用等。 
1、用hive分析业务数据即可 
2、将数据导入到hive中 
sql的设计思路:多表关联 
1、找到所有在2015-01-01到2015-01-31时间内访问A页面的用户 
2、在这些用户中删选在2015-01-01到2015-03-31下单的用户 
3、统计总数 
3.2. 你们数据库怎么导入hive 的,有没有出现问题 
在导入hive的时候,如果数据库中有blob或者text字段,会报错,解决方案在sqoop笔记中 
在将数据由Oracle数据库导入到Hive时,发现带有clob字段的表的数据会错乱,出现一些字段全为NULL的空行。 
由于在项目中CLOB字段没有实际的分析用途,因此考虑将CLOB字段去掉。 
同时,为了防止CLOB字段产生一些问题,因此将HIVE中CLOB字段禁用,禁用的方式如下: 
[Hadoop@master sqoop-1.4.5]cdcdSQOOP_HOME/conf 
[hadoop@master conf]$ vi oraoop-site.xml 
将以下属性的注释去掉,并且将value改为true 

oraoop.import.omit.lobs.and.long 
true 
If true, OraOop will omit BLOB, CLOB, NCLOB and LONG columns during an Import. 


有些表中虽然有clob字段,但是不能排除掉,因为其他字段使我们所需要,因此在导入的时候采用指定–columns的方式来进行导入 
sqoop import –hive-import –hive-database test –create-hive-table –connect jdbc –username user–password user 
–bindir //scratch –outdir /Java –table aaa –columns “ID,NAME” -m 1 –null-string ‘\N’ –null-non-string ‘\N’ 
3.3. 公司技术选型可能利用storm 进行实时计算,讲解一下storm 
描述下storm的设计模式,是基于work、excutor、task的方式运行代码,由spout、bolt组成等等 
3.4. 一个datanode 宕机,怎么一个流程恢复 
将datanode数据删除,重新当成新节点加入即可。 
3.5. Hbase 的特性,以及你怎么去设计 rowkey 和 columnFamily ,怎么去建一个table 
hbase是列式数据库,rowkey是字典序的,设计时的规则同上。 
每个列族是一个文件,将经常一起查询的列放到同一个列族中,减少文件的寻址时间。 
3.6. Redis,传统数据库,hbase,hive 每个之间的区别 
redis:分布式缓存,强调缓存,内存中数据 
传统数据库:注重关系 
hbase:列式数据库,无法做关系数据库的主外键,用于存储海量数据,底层基于hdfs 
hive:数据仓库工具,底层是mapreduce。不是数据库,不能用来做用户的交互存储 
3.7. shuffle 阶段,你怎么理解的 
shuffle过程包括在Map和Reduce两端中。 
在Map端的shuffle过程是对Map的结果进行分区(partition)、排序(sort)和分割(spill),然后将属于同一个划分的输出合并在一起 
(merge)并写在硬盘上,同时按照不同的划分将结果发送给对应的Reduce(Map输出的划分与Reduce的对应关系由JobTracker确定)。 
Reduce端又会将各个Map送来的属于同一个划分的输出进行合并(merge),然后对merge的结果进行排序,最后交给Reduce处理。通俗的讲, 
就是对Map输出结果先进行分区(partition),如“aaa”经过Partitioner后返回0,也就是这对值应当交由第一个reducer来处理。接下来, 
需要将数据写入内存缓冲区中,缓冲区的作用是批量收集map结果,减少磁盘IO的影响。我们的key/value对以及Partition的结果都会被写 
入缓冲区。当然写入之前,key与value值都会被序列化成字节数组。这个内存缓冲区是有大小限制的,默认是100MB。当map task的输出结果 
很多时,需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill。 
Spill可以认为是一个包括Sort和Combiner(Combiner是可选的,用户如果定义就有)的过程。先进行sort可以把缓冲区中一段范围key的数 
据排在一起,(如果数据多的时候,多次刷新往内存缓冲区中写入的数据可能会有属于相同范围的key,也就是说,多个spill文件中可能会 
有统一范围的key,这就是需要下面Map端merge的原因),这里有点绕,具体的介绍可以看下面的详细过程,执行过sort之后,如果用户定义 
了combiner就会执行combine,然后执行merge操作,接着就是Reduce端。

3.8. Mapreduce 的 map 数量 和 reduce 数量 怎么确定 ,怎么配置 
map的数量由数据块决定,reduce数量随便配置。 
3.9. 唯一难住我的是他说实时计算,storm 如果碰上了复杂逻辑,需要算很长的时间,你怎么去优化,怎么保证实时性 
3.10. Hive 你们用的是外部表还是内部表,有没有写过UDF,hive 的版本 
3.11. Hadoop 的版本 
1.04、1.20都为稳定版,是两个常用的hadoop1版本。 
3.12. 实时流式计算 的结果内容有哪些,你们需要统计出来么 
3.13. 
1)设计日志收集分析系统 
日志分布在各个业务系统中,我们需要对当天的日志进行实时汇总统计,同时又能按天查询历史的汇总数据(可以围绕PV、UV、IP等 
指标进行阐述) 
1、通过flume将不同系统的日志收集到kafka中 
2、通过storm实时的处理PV、UV、IP 
3、通过kafka的consumer将日志生产到hbase中。 
4、通过离线的mapreduce或者hive,处理hbase中的数据 
2)如果你来做技术分享,你会选择什么主题,课程安排是什么样的? 
大体分为3个部分: 
1、离线hadoop技术分享(mapreduce、hive) 
2、nosql数据库hbase分享 
3、实时流计算分享 
5)Hive语句实现WordCount。 
假设数据存放在hadoop下,路径为:/home/hadoop/worddata里面全是一些单词 
1、建表 
2、分组(group by)统计wordcount 
select word,count(1) from table1 group by word; 
6)给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,找出a、b文件共同的url? 
可以估计每个文件的大小为50亿×64=298G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。 
1、将文件存储到hdfs中,这样每个文件为64M或者是128M 
2、分别对两个文件的url进行去重、排序输出,这样能排除a文件中相同的url,b文件也一样 
3、对a、b两个文件处理后的结果进行wordcount,并且在reduce中判断单词个数,个数为2的时候输出,这样就找到了a、b文件中的相同url。 
4、此计算步骤中的每一步加载到内存中的文件大小都不会超过64M,远远小于4G。 
7)一亿个数据获取前100个最大值(步骤及算法复杂度) 
两种思路: 
1. 根据快速排序划分的思想 
a. 假设数组为 array[N] (N = 1 亿),首先利用quicksort的原理把array分成两个部分,左边部分比 array[N - 1] (array中的最后一个值, 
即pivot) 大, 右边部分比pivot 小。然后,可以得到 array[array.length - 1] (即 pivot) 在整个数组中的位置,假设是 k. 
b. 如果 k 比 99 大,我们在数组[0, k - 1]里找前 100 最大值。 (继续递归) 
c. 如果 k 比 99 小, 我们在数组[k + 1, …, N ]里找前 100 - (k + 1) 最大值。(继续递归) 
d. 如果 k == 99, 那么数组的前 100 个值一定是最大的。(退出) 
2.先取出前100个数,维护一个100个数的最小堆,遍历一遍剩余的元素,在此过程中维护堆就可以了。具体步骤如下: 
step1:取前m个元素(例如m=100),建立一个小顶堆。保持一个小顶堆得性质的步骤,运行时间为O(lgm);建立一个小顶堆运行时间为m*O(lgm)=O(m lgm); 
step2:顺序读取后续元素,直到结束。每次读取一个元素,如果该元素比堆顶元素小,直接丢弃 
如果大于堆顶元素,则用该元素替换堆顶元素,然后保持最小堆性质。最坏情况是每次都需要替换掉堆顶的最小元素,因此需要维护堆的代价为(N-m)*O(lgm); 
最后这个堆中的元素就是前最大的10W个。时间复杂度为O(N lgm)。 
两种思路比较: 
基于最小堆方法运行时间很稳定(每次运行时间相差很小),基于quicksort原理的方法运行时间不稳定(每次运行时间相差大)。 
Random rand = new Random(); PriorityQueue P = new PriorityQueue(); for(int i = 0,num = rand.nextInt(); (i < 100 && P.add(num)) || (i < 100000000 && (P.peek() < num && P.add(num) && P.remove() != null || 1==1)); 
i++,num = rand.nextInt()); //System.out.println(P); 
top K问题 
在大规模数据处理中,经常会遇到的一类问题:在海量数据中找出出现频率最好的前k个数,或者从海量数据中找出最大的前k个数,这类问题通常被称为top K问题。例如,在搜索引擎中, 
统计搜索最热门的10个查询词;在歌曲库中统计下载最高的前10首歌等。 
针对top K类问题,通常比较好的方案是分治+Trie树/hash+小顶堆(就是上面提到的最小堆),即先将数据集按照Hash方法分解成多个小数据集, 
然后使用Trie树活着Hash统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出现频率最高的前K个数,最后在所有top K中求出最终的top K。 
eg:有1亿个浮点数,如果找出期中最大的10000个? 
最容易想到的方法是将数据全部排序,然后在排序后的集合中进行查找,最快的排序算法的时间复杂度一般为O(nlogn),如快速排序。但是在32位的机器上, 
每个float类型占4个字节,1亿个浮点数就要占用400MB的存储空间,对于一些可用内存小于400M的计算机而言,很显然是不能一次将全部数据读入内存进行排序的。 
其实即使内存能够满足要求(我机器内存都是8GB),该方法也并不高效,因为题目的目的是寻找出最大的10000个数即可,而排序却是将所有的元素都排序了,做了很多的无用功。 
第二种方法为局部淘汰法,该方法与排序方法类似,用一个容器保存前10000个数,然后将剩余的所有数字——与容器内的最小数字相比,如果所有后续的元素都比容器内的10000个数还小, 
那么容器内这个10000个数就是最大10000个数。如果某一后续元素比容器内最小数字大,则删掉容器内最小元素,并将该元素插入容器,最后遍历完这1亿个数, 
得到的结果容器中保存的数即为最终结果了。此时的时间复杂度为O(n+m^2),其中m为容器的大小,即10000。 
第三种方法是分治法,将1亿个数据分成100份,每份100万个数据,找到每份数据中最大的10000个,最后在剩下的100*10000个数据里面找出最大的10000个。如果100万数据选择足够理想, 
那么可以过滤掉1亿数据里面99%的数据。100万个数据里面查找最大的10000个数据的方法如下:用快速排序的方法,将数据分为2堆,如果大的那堆个数N大于10000个, 
继续对大堆快速排序一次分成2堆,如果大的那堆个数N大于10000个,继续对大堆快速排序一次分成2堆,如果大堆个数N小于10000个,就在小的那堆里面快速排序一次,找第10000-n大的数字; 
递归以上过程,就可以找到第1w大的数。参考上面的找出第1w大数字,就可以类似的方法找到前10000大数字了。此种方法需要每次的内存空间为10^6*4=4MB,一共需要101次这样的比较。 
第四种方法是Hash法。如果这1亿个书里面有很多重复的数,先通过Hash法,把这1亿个数字去重复,这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间, 
然后通过分治法或最小堆法查找最大的10000个数。 
第五种方法采用最小堆。首先读入前10000个数来创建大小为10000的最小堆,建堆的时间复杂度为O(mlogm)(m为数组的大小即为10000),然后遍历后续的数字,并于堆顶(最小) 
数字进行比较。如果比最小的数小,则继续读取后续数字;如果比堆顶数字大,则替换堆顶元素并重新调整堆为最小堆。整个过程直至1亿个数全部遍历完为止。然后按照中序遍历的方式输出当前 
堆中的所有10000个数字。该算法的时间复杂度为O(nmlogm),空间复杂度是10000(常数)。 
实际运行: 
实际上,最优的解决方案应该是最符合实际设计需求的方案,在时间应用中,可能有足够大的内存,那么直接将数据扔到内存中一次性处理即可,也可能机器有多个核,这样可以采用 
多线程处理整个数据集。 
下面针对不容的应用场景,分析了适合相应应用场景的解决方案。 
(1)单机+单核+足够大内存 
如果需要查找10亿个查询次(每个占8B)中出现频率最高的10个,考虑到每个查询词占8B,则10亿个查询次所需的内存大约是10^9 * 8B=8GB内存。如果有这么大内存,直接在内存中对 
查询次进行排序,顺序遍历找出10个出现频率最大的即可。这种方法简单快速,使用。然后,也可以先用HashMap求出每个词出现的频率,然后求出频率最大的10个词。 
(2)单机+多核+足够大内存 
这时可以直接在内存总使用Hash方法将数据划分成n个partition,每个partition交给一个线程处理,线程的处理逻辑同(1)类似,最后一个线程将结果归并。 
该方法存在一个瓶颈会明显影响效率,即数据倾斜。每个线程的处理速度可能不同,快的线程需要等待慢的线程,最终的处理速度取决于慢的线程。而针对此问题,解决的方法是, 
将数据划分成c×n个partition(c>1),每个线程处理完当前partition后主动取下一个partition继续处理,知道所有数据处理完毕,最后由一个线程进行归并。 
(3)单机+单核+受限内存 
这种情况下,需要将原数据文件切割成一个一个小文件,如次啊用hash(x)%M,将原文件中的数据切割成M小文件,如果小文件仍大于内存大小,继续采用Hash的方法对数据文件进行分割, 
知道每个小文件小于内存大小,这样每个文件可放到内存中处理。采用(1)的方法依次处理每个小文件。 
(4)多机+受限内存 
这种情况,为了合理利用多台机器的资源,可将数据分发到多台机器上,每台机器采用(3)中的策略解决本地的数据。可采用hash+socket方法进行数据分发。 
从实际应用的角度考虑,(1)(2)(3)(4)方案并不可行,因为在大规模数据处理环境下,作业效率并不是首要考虑的问题,算法的扩展性和容错性才是首要考虑的。 
算法应该具有良好的扩展性,以便数据量进一步加大(随着业务的发展,数据量加大是必然的)时,在不修改算法框架的前提下,可达到近似的线性比;算法应该具有容错性, 
即当前某个文件处理失败后,能自动将其交给另外一个线程继续处理,而不是从头开始处理。 
top K问题很适合采用MapReduce框架解决,用户只需编写一个Map函数和两个Reduce 函数,然后提交到Hadoop(采用Mapchain和Reducechain)上即可解决该问题。 
具体而言,就是首先根据数据值或者把数据hash(MD5)后的值按照范围划分到不同的机器上,最好可以让数据划分后一次读入内存,这样不同的机器负责处理不同的数值范围, 
实际上就是Map。得到结果后,各个机器只需拿出各自出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是Reduce过程。 
对于Map函数,采用Hash算法,将Hash值相同的数据交给同一个Reduce task;对于第一个Reduce函数,采用HashMap统计出每个词出现的频率,对于第二个Reduce 函数,统计所有Reduce task, 
输出数据中的top K即可。 
直接将数据均分到不同的机器上进行处理是无法得到正确的结果的。因为一个数据可能被均分到不同的机器上,而另一个则可能完全聚集到一个机器上,同时还可能存在具有相同数目的数据。 
以下是一些经常被提及的该类问题。 
(1)有10000000个记录,这些查询串的重复度比较高,如果除去重复后,不超过3000000个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。请统计最热门的10个查询串, 
要求使用的内存不能超过1GB。 
(2)有10个文件,每个文件1GB,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。按照query的频度排序。 
(3)有一个1GB大小的文件,里面的每一行是一个词,词的大小不超过16个字节,内存限制大小是1MB。返回频数最高的100个词。 
(4)提取某日访问网站次数最多的那个IP。 
(5)10亿个整数找出重复次数最多的100个整数。 
(6)搜索的输入信息是一个字符串,统计300万条输入信息中最热门的前10条,每次输入的一个字符串为不超过255B,内存使用只有1GB。 
(7)有1000万个身份证号以及他们对应的数据,身份证号可能重复,找出出现次数最多的身份证号。 
重复问题 
在海量数据中查找出重复出现的元素或者去除重复出现的元素也是常考的问题。针对此类问题,一般可以通过位图法实现。例如,已知某个文件内包含一些电话号码,每个号码为8位数字, 
统计不同号码的个数。 
本题最好的解决方法是通过使用位图法来实现。8位整数可以表示的最大十进制数值为99999999。如果每个数字对应于位图中一个bit位,那么存储8位整数大约需要99MB。因为1B=8bit, 
所以99Mbit折合成内存为99/8=12.375MB的内存,即可以只用12.375MB的内存表示所有的8位数电话号码的内容。 
8)实时数据统计会用到哪些技术,它们各自的应用场景及区别是什么? 
flume:日志收集系统,主要用于系统日志的收集 
kafka:消息队列,进行消息的缓存和系统的解耦 
storm:实时计算框架,进行流式的计算。 
1)String和StringBuffer的区别,StringBuffer与StringBuilder的区别 
简单地说,就是一个变量和常量的关系。StringBuffer对象的内容可以修改;而String对象一旦产生后就不可以被修改,重新赋值其实是两个对象。 
StringBuilder:线程非安全的 
StringBuffer:线程安全的 
   当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。 
当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。

 

 

2)HashMap和HashTable,ArrayList和Vector,ArrayList和LinkedList的区别

1 HashMap不是线程安全的
hashmap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。
HashMap允许null key和null value,而hashtable不允许。
2  HashTable是线程安全的一个Collection。
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),
由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。 HashMap把
Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。 Hashtable继承自
Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,
在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 Hashtable和HashMap采用的
hash/rehash算法都大概一样,所以性能不会有很大的差别。
public static void main(String args[]) { HashTable h=new HashTable(); h.put(“用户1”,new Integer(90)); 
h.put(“用户2”,new Integer(50)); h.put(“用户3”,new Integer(60)); h.put(“用户4”,new Integer(70)); 
h.put(“用户5”,new Integer(80)); Enumeration e=h.elements(); while(e.hasMoreElements()){ System.out.println(e.nextElement()); }

总结:

 

hashmap

线程不安全

允许有null的键和值

效率高一点、

方法不是Synchronize的要提供外同步

有containsvalue和containsKey方法

HashMap 是Java1.2引进的Map interface的一个实现

HashMap是Hashtable的轻量级实现

hashtable

线程安全

不允许有null的键和值

效率稍低、

方法是是Synchronize的

有contains方法方法

、Hashtable 继承于Dictionary类

Hashtable 比HashMap要旧

 

Vector & ArrayList 
1)  Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,
因此,ArrayList的性能比Vector好。 
2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利
于节约内存空间。
 linkedlist & ArrayList 
ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦
LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引  但是缺点就是查找非常麻烦 要丛第一个索引
开始
Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2
引进的Map接口的一个实现。 
也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多
线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的
synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的
HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多
处理费用。 
第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key,但任意数量的
条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。
如果有必要,用containKey()方法来区别这两种情况。 
一些资料建议,当需要同步时,用Hashtable,反之用HashMap。但是,因为在需要时,HashMap可以被同步,HashMap的功能比Hashtable的
功能更多,而且它不是基于一个陈旧的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。 
关于Properties 
有时侯,你可能想用一个hashtable来映射key的字符串到value的字符串。DOS、Windows和Unix中的环境字符串就有一些例子,如key的字符串
PATH被映射到value的字符串C:\WINDOWS;C:\WINDOWS\SYSTEM。Hashtables是表示这些的一个简单的方法,但Java提供了另外一种方法。 
Java.util.Properties类是Hashtable的一个子类,设计用于String keys和values。Properties对象的用法同Hashtable的用法相象,但是类
增加了两个节省时间的方法,你应该知道。 
Store()方法把一个Properties对象的内容以一种可读的形式保存到一个文件中。Load()方法正好相反,用来读取文件,并设定Properties
对象来包含keys和values。 
注意,因为Properties扩展了Hashtable,你可以用超类的put()方法来添加不是String对象的keys和values。这是不可取的。另外,如果你将
store()用于一个不包含String对象的Properties对象,store()将失败。作为put()和get()的替代,你应该用setProperty()和getProperty(),它们用String参数。

3)多线程实现方式Thread和Runnable的区别?
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承
了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承
了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
下面看例子:

 

[java] view plain copy

  1. package org.thread.demo;    
  2. class MyThread extends Thread{    
  3.     private String name;    
  4.     public MyThread(String name) {    
  5.         super();    
  6.         this.name = name;    
  7.     }    
  8.     public void run(){    
  9.         for(int i=0;i<10;i++){    
  10.             System.out.println(”线程开始:”+this.name+“,i=”+i);    
  11.         }    
  12.     }    
  13. }    
  14.    
  15. package org.thread.demo;    
  16.    
  17. public class ThreadDemo01 {    
  18.    
  19.     public static void main(String[] args) {    
  20.         MyThread mt1=new MyThread(“线程a”);    
  21.         MyThread mt2=new MyThread(“线程b”);    
  22.         mt1.run();    
  23.         mt2.run();    
  24.     }    
  25. }  
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();  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25


但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,
则会通过JVM找到run()方法。下面启动start()方法启动线程:

 

 

[java] view plain copy

  1. package org.thread.demo;    
  2. public class ThreadDemo01 {    
  3.     public static void main(String[] args) {    
  4.         MyThread mt1=new MyThread(“线程a”);    
  5.         MyThread mt2=new MyThread(“线程b”);    
  6.         mt1.start();    
  7.         mt2.start();    
  8.     }    
  9. }  
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();  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9


这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?
在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,可以发现此方法中使用了private 
native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)
Runnable接口
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。

 

 

[java] view plain copy

  1. public interface Runnable{    
  2.     public void run();    
  3. }  
public interface Runnable{  
    public void run();  
}
  • 1
  • 2
  • 3


例子:

 

 

[java] view plain copy

  1. package org.runnable.demo;    
  2. class MyThread implements Runnable{    
  3.     private String name;    
  4.     public MyThread(String name) {    
  5.         this.name = name;    
  6.     }  
  7.     public void run(){    
  8.         for(int i=0;i<100;i++){    
  9.             System.out.println(”线程开始:”+this.name+“,i=”+i);    
  10.         }    
  11.     }    
  12.    
  13. }  
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);  
        }  
    }  

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13


但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,
有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable
实现的多线程。(start()可以协调系统的资源):

 

 

[java] view plain copy

  1. package org.runnable.demo;    
  2. import org.runnable.demo.MyThread;    
  3. public class ThreadDemo01 {    
  4.     public static void main(String[] args) {    
  5.         MyThread mt1=new MyThread(“线程a”);    
  6.         MyThread mt2=new MyThread(“线程b”);    
  7.         new Thread(mt1).start();    
  8.         new Thread(mt2).start();    
  9.     }    
  10. }  
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();  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10


两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
避免点继承的局限,一个类可以继承多个接口。
适合于资源的共享
以卖票程序为例,通过Thread类完成:

 

 

[java] view plain copy

  1. package org.demo.dff;    
  2. class MyThread extends Thread{    
  3.     private int ticket=10;    
  4.     public void run(){    
  5.         for(int i=0;i<20;i++){    
  6.             if(this.ticket>0){    
  7.                 System.out.println(”卖票:ticket”+this.ticket–);    
  8.             }    
  9.         }    
  10.     }    
  11. }  
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--);  
            }  
        }  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11


下面通过三个线程对象,同时卖票:

 

 

[java] view plain copy

  1. package org.demo.dff;    
  2. public class ThreadTicket {    
  3.     public static void main(String[] args) {    
  4.         MyThread mt1=new MyThread();    
  5.         MyThread mt2=new MyThread();    
  6.         MyThread mt3=new MyThread();    
  7.         mt1.start();//每个线程都各卖了10张,共卖了30张票    
  8.         mt2.start();//但实际只有10张票,每个线程都卖自己的票    
  9.         mt3.start();//没有达到资源共享    
  10.     }    
  11. }  
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();//没有达到资源共享  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11


如果用Runnable就可以实现资源共享,下面看例子:

 

 

[java] view plain copy

  1. package org.demo.runnable;    
  2. class MyThread implements Runnable{    
  3.     private int ticket=10;    
  4.     public void run(){    
  5.         for(int i=0;i<20;i++){    
  6.             if(this.ticket>0){    
  7.                 System.out.println(”卖票:ticket”+this.ticket–);    
  8.             }    
  9.         }    
  10.     }    
  11. }    
  12.    
  13. package org.demo.runnable;    
  14.    
  15. public class RunnableTicket {    
  16.    
  17.     public static void main(String[] args) {    
  18.         MyThread mt=new MyThread();    
  19.         new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一    
  20.         new Thread(mt).start();//个实例化对象mt,就会出现异常    
  21.         new Thread(mt).start();    
  22.     }    
  23. };  
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实现多线程可以达到资源共享目的。

 

 

3.14
1、一个Hadoop环境,整合了HBase和Hive,是否有必要给HDFS和Hbase都分别配置压缩策略?请给出对压缩策略的建议。
hdfs在存储的时候不会将数据进行压缩,如果想进行压缩,我们可以在向hdfs上传数据的时候进行压缩。
1)、  采用压缩流

[java] view plain copy

  1. //压缩文件  
  2. public static void compress(String codecClassName) throws Exception{  
  3.     Class codecClass = Class.forName(codecClassName);  
  4.     Configuration conf = new Configuration();  
  5.     FileSystem fs = FileSystem.get(conf);  
  6.     CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, conf);  
  7.     //指定压缩文件路径  
  8.     FSDataOutputStream outputStream = fs.create(new Path(“/user/hadoop/text.gz”));  
  9.     //指定要被压缩的文件路径  
  10.     FSDataInputStream in = fs.open(new Path(“/user/hadoop/aa.txt”));  
  11.     //创建压缩输出流  
  12.     CompressionOutputStream out = codec.createOutputStream(outputStream);   
  13.     IOUtils.copyBytes(in, out, conf);  
  14.     IOUtils.closeStream(in);  
  15.     IOUtils.closeStream(out);  
  16. }  
//压缩文件
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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2)、  采用序列化文件

 

[java] view plain copy

  1. public void testSeqWrite() throws Exception {  
  2.     Configuration conf = new Configuration();// 创建配置信息  
  3.     conf.set(”fs.default.name”, “hdfs://master:9000”);// hdfs默认路径  
  4.     conf.set(”hadoop.job.ugi”, “hadoop,hadoop”);// 用户和组信息  
  5.     String uriin = ”hdfs://master:9000/ceshi2/”;// 文件路径  
  6.     FileSystem fs = FileSystem.get(URI.create(uriin), conf);// 创建filesystem  
  7.     Path path = new Path(“hdfs://master:9000/ceshi3/test.seq”);// 文件名  
  8.     IntWritable k = new IntWritable();// key,相当于int  
  9.     Text v = new Text();// value,相当于String  
  10.     SequenceFile.Writer w = SequenceFile.createWriter(fs, conf, path,k.getClass(), v.getClass());// 创建writer  
  11.     for (int i = 1; i < 100; i++) {// 循环添加  
  12.         k.set(i);  
  13.         v.set(”abcd”);  
  14.         w.append(k, v);  
  15.     }  
  16.     w.close();  
  17.     IOUtils.closeStream(w);// 关闭的时候flush  
  18.     fs.close();  
  19. }  
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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19


hbase为列存数据库,本身存在压缩机制,所以无需设计。

 

3、简述Hbase性能优化的思路

1)、  在库表设计的时候,尽量考虑rowkey和columnfamily的特性
2)、  进行hbase集群的调优:见hbase调优

4、简述Hbase filter的实现原理是什么?结合实际项目经验,写出几个使用filter的场景。

hbase的filter是通过scan设置的,所以是基于scan的查询结果进行过滤。
1)、在进行订单开发的时候,我们使用rowkeyfilter过滤出某个用户的所有订单
2)、在进行云笔记开发时,我们使用rowkey过滤器进行redis数据的恢复。

5、ROWKEY的后缀匹配怎么实现?例如ROWKEY是yyyyMMDD-UserID形式,如果要以UserID为条件查询数据,怎样实现。
使用rowkey过滤器实现
6、简述Hive中的虚拟列作用是什么,使用它的注意事项。
Hive提供了三个虚拟列:
INPUT__FILE__NAME
BLOCK__OFFSET__INSIDE__FILE
ROW__OFFSET__INSIDE__BLOCK
但ROW__OFFSET__INSIDE__BLOCK默认是不可用的,需要设置hive.exec.rowoffset为true才可以。可以用来排查有问题的输入数据。
INPUT__FILE__NAME, mapper任务的输出文件名。
BLOCK__OFFSET__INSIDE__FILE, 当前全局文件的偏移量。对于块压缩文件,就是当前块的文件偏移量,即当前块的第一个字节在文件中的偏移量。
hive> SELECT INPUT__FILE__NAME, BLOCK__OFFSET__INSIDE__FILE, line
> FROM hive_text WHERE line LIKE ‘%hive%’ LIMIT 2;
har://file/user/hive/warehouse/hive_text/folder=docs/
data.har/user/hive/warehouse/hive_text/folder=docs/README.txt  2243
har://file/user/hive/warehouse/hive_text/folder=docs/
data.har/user/hive/warehouse/hive_text/folder=docs/README.txt  3646
7、如果要存储海量的小文件(大小都是几百K~几M),请简述自己的设计方案。
1)、将小文件打成har文件存储
2)、将小文件序列化到hdfs中
8、有两个文本文件,文件中的数据按行存放,请编写MapReduce程序,找到两个文件中彼此不相同的行。
写个mapreduce链  用依赖关系,一共三个mapreduce,第一个处理第一个文件,第二个处理第二个文件,第三个处理前两个的输出结果,
第一个mapreduce将文件去重,第二个mapreduce也将文件去重,第三个做wordcount,wordcount为1的结果就是不同的。

 

4.   共同朋友

mapred找共同朋友,数据格式如下
usr:friend,friend,friend…
—————
A:B,C,D,E,F
B:A,C,D,E
C:A,B,E
D:A,B,E
E:A,B,C,D
F:A
第一个字母表示本人,其他是他的朋友,找出共同朋友的人,和共同朋友是谁。
思路:例如A,他的朋友是B\C\D\E\F\,那么BC的共同朋友就是A。所以将BC作为key,将A作为value,在map端输出即可!其他的朋友循环处理。
[java] view plain copy

  1. import java.io.IOException;  
  2. import java.util.Set;  
  3. import java.util.StringTokenizer;  
  4. import java.util.TreeSet;  
  5. import org.apache.hadoop.conf.Configuration;  
  6. import org.apache.hadoop.fs.Path;  
  7. import org.apache.hadoop.io.Text;  
  8. import org.apache.hadoop.mapreduce.Job;  
  9. import org.apache.hadoop.mapreduce.Mapper;  
  10. import org.apache.hadoop.mapreduce.Reducer;  
  11. import org.apache.hadoop.mapreduce.Mapper.Context;  
  12. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  13. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  14. import org.apache.hadoop.util.GenericOptionsParser;  
  15.    
  16. public class FindFriend {     
  17.    
  18.     public static class ChangeMapper extends Mapper{                        
  19.         @Override  
  20.         public void map(Object key, Text value, Context context) throws IOException, InterruptedException {  
  21.             StringTokenizer itr = new StringTokenizer(value.toString());  
  22.             Text owner = new Text();  
  23.             Set set = new TreeSet();  
  24.             owner.set(itr.nextToken());  
  25.             while (itr.hasMoreTokens()) {  
  26.                 set.add(itr.nextToken());  
  27.             }               
  28.             String[] friends = new String[set.size()];  
  29.             friends = set.toArray(friends);   
  30.             for(int i=0;i
  31.                 for(int j=i+1;j
  32.                     String outputkey = friends[i]+friends[j];          
  33.                     context.write(new Text(outputkey),owner);  
  34.                 }                                       
  35.             }  
  36.         }  
  37.     }              
  38.    
  39.     public static class FindReducer extends Reducer{                            
  40.         public void reduce(Text key, Iterable values, Context context) throws IOException,InterruptedException {  
  41.             String  commonfriends =”“;   
  42.             for (Text val : values) {  
  43.                 if(commonfriends == “”){  
  44.                     commonfriends = val.toString();  
  45.                 }else{  
  46.                     commonfriends = commonfriends+”:”+val.toString();  
  47.                 }  
  48.             }  
  49.             context.write(key, new Text(commonfriends));   
  50.         }                             
  51.     }  
  52.    
  53.     public static void main(String[] args) throws IOException,InterruptedException, ClassNotFoundException{               
  54.         Configuration conf = new Configuration();  
  55.         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();  
  56.         if (otherArgs.length < 2) {  
  57.             System.err.println(”args error”);  
  58.             System.exit(2);  
  59.         }  
  60.         Job job = new Job(conf, “word count”);  
  61.         job.setJarByClass(FindFriend.class);  
  62.         job.setMapperClass(ChangeMapper.class);  
  63.         job.setCombinerClass(FindReducer.class);  
  64.         job.setReducerClass(FindReducer.class);  
  65.         job.setOutputKeyClass(Text.class);  
  66.         job.setOutputValueClass(Text.class);  
  67.         for (int i = 0; i < otherArgs.length - 1; ++i) {  
  68.             FileInputFormat.addInputPath(job, new Path(otherArgs[i]));  
  69.         }  
  70.         FileOutputFormat.setOutputPath(job,new Path(otherArgs[otherArgs.length - 1]));  
  71.         System.exit(job.waitForCompletion(true) ? 0 : 1);                  
  72.     }  
  73. }  
import java.io.IOException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;

public class FindFriend {   

    public static class ChangeMapper extends Mapper{                      
        @Override
        public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
            StringTokenizer itr = new StringTokenizer(value.toString());
            Text owner = new Text();
            Set set = new TreeSet();
            owner.set(itr.nextToken());
            while (itr.hasMoreTokens()) {
                set.add(itr.nextToken());
            }             
            String[] friends = new String[set.size()];
            friends = set.toArray(friends); 
            for(int i=0;i{                          
        public void reduce(Text key, Iterable values, Context context) throws IOException,InterruptedException {
            String  commonfriends =""; 
            for (Text val : values) {
                if(commonfriends == ""){
                    commonfriends = val.toString();
                }else{
                    commonfriends = commonfriends+":"+val.toString();
                }
            }
            context.write(key, new Text(commonfriends)); 
        }                           
    }

    public static void main(String[] args) throws IOException,InterruptedException, ClassNotFoundException{             
        Configuration conf = new Configuration();
        String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
        if (otherArgs.length < 2) {
            System.err.println("args error");
            System.exit(2);
        }
        Job job = new Job(conf, "word count");
        job.setJarByClass(FindFriend.class);
        job.setMapperClass(ChangeMapper.class);
        job.setCombinerClass(FindReducer.class);
        job.setReducerClass(FindReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);
        for (int i = 0; i < otherArgs.length - 1; ++i) {
            FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
        }
        FileOutputFormat.setOutputPath(job,new Path(otherArgs[otherArgs.length - 1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);                
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

结果:

1. AB      E:C:D

2. AC      E:B

3. AD      B:E

4. AE      C:B:D

5. BC      A:E

6. BD      A:E

7. BE      C:D:A

8. BF      A

9. CD      E:A:B

10. CE      A:B

11. CF      A

12. DE      B:A

13. DF      A

14. EF      A

5.   基站逗留时间

需求:

期望:

思路:

将数据导入hive表中,查询时,用电话号码和时间排序即可!

 

6.   脚本替换

 

脚本:随意命名为aaa.sh

#!/bin/bash

ls $1 | while read line

do

sed -i ‘s,$HADOOP_HOME$,\/home\/aa,g’ 11line

echo 11line

done

脚本执行命令:替换/home/hadoop/test/下的所有文件
./aaa.sh /home/hadoop/test/

7.   一键执行

 

 

 

脚本:

vi runRemoteCmd.sh

#!/bin/bash

$1

ssh -q hadoop@slave1 “$1”

ssh -q hadoop@slave2 “$1”

 

 

 

 

执行命令

 



 

./runRemoteCmd.sh “ls -l”

 


 

 

8.   大数据面试汇总

1.讲解一下MapReduce 的一些基本流程
任务提交流程,任务运行流程
2.你们数据库怎么导入hive 的,有没有出现问题
使用sqoop导入,我们公司的数据库中设计了text字段,导致导入的时候出现了缓存不够的情况(见云笔记),开始解决起来感觉很棘手,后来查看了sqoop的文档,加上了limit属性,解决了。
3.公司技术选型可能利用storm 进行实时计算,讲解一下storm
从storm的应用,代码书写,运行机制讲
4.问你java 集合类的数据结构,比如hashmap
看java面试宝典
5.问你知不知道concurrent 包下的东西,例如concurrenthashmap
看java面试宝典
6.公司最近主要在自然语言学习去开发,有没有接触过
没有用过

 

 

  1. 面试问题:

1.从前到后从你教育背景(学过哪些课)到各个项目你负责的模块,问的很细(本以为他是物理学博士,但是所有的技术都懂) 
2.hadoop 的 namenode 宕机,怎么解决 
先分析宕机后的损失,宕机后直接导致client无法访问,内存中的元数据丢失,但是硬盘中的元数据应该还存在,如果只是节点挂了, 
重启即可,如果是机器挂了,重启机器后看节点是否能重启,不能重启就要找到原因修复了。但是最终的解决方案应该是在设计集群的初期 
就考虑到这个问题,做namenode的HA。 
3.一个datanode 宕机,怎么一个流程恢复 
Datanode宕机了后,如果是短暂的宕机,可以实现写好脚本监控,将它启动起来。如果是长时间宕机了,那么datanode上的数据应该已经 
被备份到其他机器了,那这台datanode就是一台新的datanode了,删除他的所有数据文件和状态文件,重新启动。 
4.Hbase 的特性,以及你怎么去设计 rowkey 和 columnFamily ,怎么去建一个table 
因为hbase是列式数据库,列非表schema的一部分,所以在设计初期只需要考虑rowkey 和 columnFamily即可,rowkey有位置相关性,所以 
如果数据是练习查询的,最好对同类数据加一个前缀,而每个columnFamily实际上在底层是一个文件,那么文件越小,查询越快,所以讲经 
常一起查询的列设计到一个列簇,但是列簇不宜过多。 
5.Redis,传统数据库,hbase,hive 每个之间的区别(问的非常细) 
Redis是缓存,围绕着内存和缓存说 
Hbase是列式数据库,存在hdfs上,围绕着数据量来说 
Hive是数据仓库,是用来分析数据的,不是增删改查数据的。 
6.公司之后倾向用spark 开发,你会么(就用java代码去写) 
会,spark使用scala开发的,在scala中可以随意使用jdk的类库,可以用java开发,但是最好用原生的scala开发,兼容性好,scala更灵活。

  1. 面试问题: 
    1.笔试: java基础(基本全忘,做的很烂,复习大数据连单例都忘了怎么写) 
    复习java面试宝典 
    2.开始介绍项目,直接用大数据项目介绍,项目经理也懂大数据 
    3.Mapreduce 一些流程,经过哪些步骤 
    Map—combiner—partition—sort—copy—sort—grouping—reduce 
    4.说下对hadoop 的一些理解,包括哪些组件 
    详谈hadoop的应用,包括的组件分为三类,分别说明hdfs,yarn,mapreduce 
    5.详细讲解下你流式实时计算的项目部署以及收集的结果情况 
    讲解storm集群的部署方案,项目的大小,使用的worker数,数据收集在hbase或者hdfs,好处是什么 
    6.你的数据库是不是很大么,有没有分表,分区,你是怎么实现的 
    数据库的分表在设计初期是按照月份进行拆分的,不同的月份查询不同的表。分区没弄过。 
    7.开始问java的一些东西(从各种框架原理到各种复杂SQL) 
    8.多线程,并发,垃圾回收机制,数据结构(问这些,基本觉得看你是不是高级程序员了) 
    多线程要知道操作方式,线程安全的锁,并且要知道lock锁 
    垃圾回收机制需要详细了解(见云笔记),主要从内存划分,垃圾回收主要的工作区域,垃圾回收器的种类,各有什么优缺点, 
    用在哪里合适。 
    数据结构基本的要知道,复杂的参考相关的书籍。
  2. 面试问题: 
    1.BI小组的3个年轻学生一起技术面试(一个是南开博士) 
    2.数据量多少,集群规模多大,型号 
    一般中型的电商或者互联网企业,日志量每天在200-500M左右,集群规模在30-50台左右,机器一般为dell的2000左右的服务器,型号不定 
    大型的互联网公司据网上资料显示,日志量在GP-PB不等,集群规模在500-4000不等,甚至更多,机器型号不确定。 
    3.项目,mapreduce 
    介绍整个mapreduce项目流程,数据采集—数据聚合—数据分析—数据展示等 
    4.实时流式计算框架,几个人,多长时间,细节问题,包括讲flume ,kafka ,storm 的各个的组件组成,你负责那一块,如果需要你搭建你可以 
    完成么? 
    5.你觉得spark 可以完全替代hadoop 么?
  3. 面试问题: 
    1.一些传统的hadoop 问题,mapreduce 他就问shuffle 阶段,你怎么理解的 
    Shuffle意义在于将不同map处理后的数据进行合理分配,让reduce处理,从而产生了排序、分区。 
    2.Mapreduce 的 map 数量 和 reduce 数量 怎么确定 ,怎么配置 
    Map无法配置,reduce随便配置 
    3.唯一难住我的是他说实时计算,storm 如果碰上了复杂逻辑,需要算很长的时间,你怎么去优化 
    拆分复杂的业务到多个bolt中,这样可以利用bolt的tree将速度提升 
    4.Hive 你们用的是外部表还是内部表,有没有写过UDF(当然吹自己写过了),hive 的版本 
    外部表,udf,udaf等,hive版本为1.0 
    5.Hadoop 的版本 
    如果是1.0版本就说1.2,如果是2.0版本,就说2.6或者2.7 
    1.2为官方稳定版本,2.7为官方稳定版本。 
    Apache Hadoop 2.7.1于美国时间2015年07月06日正式发布,本版本属于稳定版本,是自Hadoop 2.6.0以来又一个稳定版,同时也是 
    Hadoop 2.7.x版本线的第一个稳定版本,也是 2.7版本线的维护版本,变化不大,主要是修复了一些比较严重的Bug 
    6.实时流式计算的结果内容有哪些,你们需要统计出来么(我就说highchart展示) 
    简单介绍日志监控、风控等结果内容,统计出来显示在报表或者邮件中。 
    7.开始问java相关,包括luecne,solr(倒排索引的原理),框架呀,redis呀
  4. 京东商城 - 大数据

(1)Java篇 
1、JVM,GC(算法,新生代,老年代),JVM结构 
2、hashcode,hashMap,list,hashSet,equals(结构原理),A extends B(类的加载顺序) 
1.父类静态代码块; 
2.子类静态代码块; 
3.父类非静态代码块; 
4.父类构造函数; 
5.子类非静态代码块; 
6.子类构造函数; 
3、多线程,主线程,次线程,唤醒,睡眠 
略 
4、常见算法:冒泡算法,排序算法,二分查找,时间复杂度 
略 
(2)Flume篇 
1、数据怎么采集到Kafka,实现方式 
使用官方提供的flumeKafka插件,插件的实现方式是自定义了flume的sink,将数据从channle中取出,通过kafka的producer写入到kafka中, 
可以自定义分区等。 
2、flume管道内存,flume宕机了数据丢失怎么解决 
1、Flume的channel分为很多种,可以将数据写入到文件 
2、防止非首个agent宕机的方法数可以做集群或者主备 
3、flume配置方式,flume集群(问的很详细) 
Flume的配置围绕着source、channel、sink叙述,flume的集群是做在agent上的,而非机器上。 
4、flume不采集Nginx日志,通过Logger4j采集日志,优缺点是什么? 
优点:Nginx的日志格式是固定的,但是缺少sessionid,通过logger4j采集的日志是带有sessionid的,而session可以通过redis共享, 
保证了集群日志中的同一session落到不同的tomcat时,sessionId还是一样的,而且logger4j的方式比较稳定,不会宕机。 
缺点:不够灵活,logger4j的方式和项目结合过于紧密,而flume的方式比较灵活,拔插式比较好,不会影响项目性能。 
5、flume和kafka采集日志区别,采集日志时中间停了,怎么记录之前的日志。 
Flume采集日志是通过流的方式直接将日志收集到存储层,而kafka试讲日志缓存在kafka集群,待后期可以采集到存储层。 
Flume采集中间停了,可以采用文件的方式记录之前的日志,而kafka是采用offset的方式记录之前的日志。 
(3)Kafka篇 
1、容错机制 
分区备份,存在主备partition 
2、同一topic不同partition分区 
???? 
3、kafka数据流向 
Producer leader partition follower partition(半数以上) consumer 
4、kafka+spark-streaming结合丢数据怎么解决? 
spark streaming从1.2开始提供了数据的零丢失,想享受这个特性,需要满足如下条件:

  1. 数据输入需要可靠的sources和可靠的receivers

  2. 应用metadata必须通过应用driver checkpoint

  3. WAL(write ahead log)

1.1. 可靠的sources和receivers

spark streaming可以通过多种方式作为数据sources(包括kafka),输入数据通过receivers接收,通过replication存储于spark中(为了faultolerance,默认复制到两个spark executors),如果数据复制完成,receivers可以知道(例如kafka中更新offsets到zookeeper中)。这样当receivers在接收数据过程中crash掉,不会有数据丢失,receivers没有复制的数据,当receiver恢复后重新接收。

1.2. metadata checkpoint

可靠的sources和receivers,可以使数据在receivers失败后恢复,然而在driver失败后恢复是比较复杂的,一种方法是通过checkpoint metadata到HDFS或者S3。metadata包括:

· configuration

· code

· 一些排队等待处理但没有完成的RDD(仅仅是metadata,而不是data)

这样当driver失败时,可以通过metadata checkpoint,重构应用程序并知道执行到那个地方。

1.3. 数据可能丢失的场景

可靠的sources和receivers,以及metadata checkpoint也不可以保证数据的不丢失,例如:

· 两个executor得到计算数据,并保存在他们的内存中

· receivers知道数据已经输入

· executors开始计算数据

· driver突然失败

· driver失败,那么executors都会被kill掉

· 因为executor被kill掉,那么他们内存中得数据都会丢失,但是这些数据不再被处理

· executor中的数据不可恢复

1.4. WAL

为了避免上面情景的出现,spark streaming 1.2引入了WAL。所有接收的数据通过receivers写入HDFS或者S3中checkpoint目录,这样当driver失败后,executor中数据丢失后,可以通过checkpoint恢复。

1.5. At-Least-Once

尽管WAL可以保证数据零丢失,但是不能保证exactly-once,例如下面场景:

· Receivers接收完数据并保存到HDFS或S3

· 在更新offset前,receivers失败了

· Spark Streaming以为数据接收成功,但是Kafka以为数据没有接收成功,因为offset没有更新到zookeeper

· 随后receiver恢复了

· 从WAL可以读取的数据重新消费一次,因为使用的kafka High-Level消费API,从zookeeper中保存的offsets开始消费

1.6. WAL的缺点

通过上面描述,WAL有两个缺点:

· 降低了receivers的性能,因为数据还要存储到HDFS等分布式文件系统

· 对于一些resources,可能存在重复的数据,比如Kafka,在Kafka中存在一份数据,在Spark Streaming也存在一份(以WAL的形式存储在hadoop API兼容的文件系统中)

1.7. Kafka direct API

为了WAL的性能损失和exactly-once,spark streaming1.3中使用Kafka direct API。非常巧妙,Spark driver计算下个batch的offsets,指导executor消费对应的topics和partitions。消费Kafka消息,就像消费文件系统文件一样。

  1. 不再需要kafka receivers,executor直接通过Kafka API消费数据

  2. WAL不再需要,如果从失败恢复,可以重新消费

  3. exactly-once得到了保证,不会再从WAL中重复读取数据

1.8. 总结

主要说的是spark streaming通过各种方式来保证数据不丢失,并保证exactly-once,每个版本都是spark streaming越来越稳定,越来越向生产环境使用发展。

5、kafka中存储目录data/dir…..topic1和topic2怎么存储的,存储结构,data…..目录下有多少个分区,每个分区的存储格式是什么样的? 
1、topic是按照“主题名-分区”存储的 
2、分区个数由配置文件决定 
3、每个分区下最重要的两个文件是0000000000.log和000000.index,0000000.log以默认1G大小回滚。 
(4)Hive篇 
1、hive partition分区 
分区表,动态分区 
2、insert into 和 override write区别? 
insert into:将某一张表中的数据写到另一张表中 
override write:覆盖之前的内容。 
3、假如一个分区的数据主部错误怎么通过hivesql删除hdfs 
alter table ptable drop partition (daytime=’20140911’,city=’bj’); 
元数据,数据文件都删除,但目录daytime= 20140911还在 
(5)Storm篇 
1、开发流程,容错机制 
开发流程: 
1、写主类(设计spout和bolt的分发机制) 
2、写spout收集数据 
3、写bolt处理数据,根据数据量和业务的复杂程度,设计并行度。 
容错机制:采用ack和fail进行容错,失败的数据重新发送。 
2、storm和spark-streaming:为什么用storm不同spark-streaming 
3、mr和spark区别,怎么理解spark-rdd 
Mr是文件方式的分布式计算框架,是将中间结果和最终结果记录在文件中,map和reduce的数据分发也是在文件中。 
spark是内存迭代式的计算框架,计算的中间结果可以缓存内存,也可以缓存硬盘,但是不是每一步计算都需要缓存的。 
Spark-rdd是一个数据的分区记录集合……………… 
4、sqoop命令

sqoop import –connect jdbc:mysql://192.168.56.204:3306/sqoop –username hive –password hive –table jobinfo –target-dir /sqoop/test7 –inline-lob-limit 16777216 –fields-terminated-by ‘\t’ -m 2

sqoop create-hive-table –connect jdbc:mysql://192.168.56.204:3306/sqoop –table jobinfo –username hive –password hive –hive-table sqtest –fields-terminated-by “\t” –lines-terminated-by “\n”;

(6)Redis篇 
1、基本操作,存储格式 
略 
(7)Mysql篇 
1、mysql集群的分布式事务 
京东自主开发分布式MYSQL集群系统 
2、mysql性能优化(数据方面) 
数据的分表、分库、分区 
(6)Hadoop篇 
1、hadoop HA 两个namenode和zk之间的通信,zk的选举机制? 
HA是通过先后获取zk的锁决定谁是主 
Zk的选举机制,涉及到全新机群的选主和数据恢复的选主 
2、mr运行机制

3、yarn流程

1) 用户向YARN 中提交应用程序, 其中包括ApplicationMaster 程序、启动ApplicationMaster 的命令、用户程序等。 
2) ResourceManager 为该应用程序分配第一个Container, 并与对应的NodeManager 通信,要求它在这个Container 中启动应用程序 
的ApplicationMaster。 
3) ApplicationMaster 首先向ResourceManager 注册, 这样用户可以直接通过ResourceManage 查看应用程序的运行状态,然后它将 
为各个任务申请资源,并监控它的运行状态,直到运行结束,即重复步骤4~7。 
4) ApplicationMaster 采用轮询的方式通过RPC 协议向ResourceManager 申请和领取资源。 
5) 一旦ApplicationMaster 申请到资源后,便与对应的NodeManager 通信,要求它启动任务。 
6) NodeManager 为任务设置好运行环境(包括环境变量、JAR 包、二进制程序等)后,将任务启动命令写到一个脚本中,并通过运行 
该脚本启动任务。 
7) 各个任务通过某个RPC 协议向ApplicationMaster 汇报自己的状态和进度,以让ApplicationMaster 随时掌握各个任务的运行状态, 
从而可以在任务失败时重新启动任务。在应用程序运行过程中,用户可随时通过RPC 向ApplicationMaster 查询应用程序的当前运行状态。 
8) 应用程序运行完成后,ApplicationMaster 向ResourceManager 注销并关闭自己。 
(7)Hbase 
1、涉及到概念,文档 
(8)Spark篇 
1、spark原理

Spark应用转换流程

1、 spark应用提交后,经历了一系列的转换,最后成为task在每个节点上执行

2、 RDD的Action算子触发Job的提交,生成RDD DAG

3、 由DAGScheduler将RDD DAG转化为Stage DAG,每个Stage中产生相应的Task集合

4、 TaskScheduler将任务分发到Executor执行

5、 每个任务对应相应的一个数据块,只用用户定义的函数处理数据块

Driver运行在Worker上

通过org.apache.spark.deploy.Client类执行作业,作业运行命令如下:

作业执行流程描述:

1、客户端提交作业给Master

2、Master让一个Worker启动Driver,即SchedulerBackend。Worker创建一个DriverRunner线程,DriverRunner启动SchedulerBackend进程。

3、另外Master还会让其余Worker启动Exeuctor,即ExecutorBackend。Worker创建一个ExecutorRunner线程,ExecutorRunner会启动ExecutorBackend进程。

4、ExecutorBackend启动后会向Driver的SchedulerBackend注册。SchedulerBackend进程中包含DAGScheduler,它会根据用户程序,生成执行计划,并调度执行。对于每个stage的task,都会被存放到TaskScheduler中,ExecutorBackend向SchedulerBackend汇报的时候把TaskScheduler中的task调度到ExecutorBackend执行。

5、所有stage都完成后作业结束。

Driver运行在客户端

作业执行流程描述:

1、客户端启动后直接运行用户程序,启动Driver相关的工作:DAGScheduler和BlockManagerMaster等。

2、客户端的Driver向Master注册。

3、Master还会让Worker启动Exeuctor。Worker创建一个ExecutorRunner线程,ExecutorRunner会启动ExecutorBackend进程。

4、ExecutorBackend启动后会向Driver的SchedulerBackend注册。Driver的DAGScheduler解析作业并生成相应的Stage,每个Stage包含的Task通过TaskScheduler分配给Executor执行。

5、所有stage都完成后作业结束。

你可能感兴趣的:(面试题)