大数据·Java面试题红宝书

说在前面

你好! 各位萌新!
阳光明媚风清云淡,是这个“金三银四”季节里最美好的回忆。
正是这个招聘热潮,激励着每一个怀揣梦想的人儿奋斗不息,拼搏不止。
找到一个让自己满意的工作,是一件多么让自己高兴的事情。
而每一次面试,面对不同项目组不同需求,不同侧重,我们又是在一次又一次的挫折中不断成长。
作者在找到工作前,几乎每一天都在面试。
面对不同的面试官,不同的面试题,有过潇潇洒洒谈吐优雅,有过支支吾吾语塞脸红。
这些也都积淀下来成为了技术研究路上不可磨灭的回忆。
本着互相成就的精神,这里写下来历次面试中,面试官的真实面试案例,以及不完整的解析,不足之处请指正。
面试题目侧重不同,难易度也不尽相同,希望能够帮助辛勤拼搏的每一个求职者,祝大家好运。
写于2019/03/21,家中。

大数据面试题

Hadoop和Spark运行模式有哪些?

Spark运行模式有四种,分别是:

  1. 本地模式:Spark单机运行,一般用于开发测试。
  2. Standalone模式:构建一个由Master+Slave构成的Spark集群,Spark运行在集群中。
  3. Spark on Yarn模式:Spark客户端直接连接Yarn。不需要额外构建Spark集群。
  4. Spark on Mesos模式:Spark客户端直接连接Mesos。不需要额外构建Spark集群。

Hadoop运行模式有三种,分别是:

  1. 单机模式。
  2. 伪分布式。
  3. 完全分布式。

工作中用的复杂一点的MR

略。

HDFS的主要节点

HDFS中主要节点有三个,分别是:1)NameNode;2)SecondaryNameNode;3)DataNode。

  1. NameNode:NameNode维护着HDFS中的元数据信息,包括文件和Block之间关系的信息、Block数量信息、Block和DataNode
    之间的关系信息。
    NameNode中的元数据信息存储在内存以及文件中,内存中为实时信息,文件中为数据镜像作为持久化存储使用。
    文件包括:
    fsimage 元数据镜像文件。存储某NameNode元数据信息,并不是实时同步内存中的数据。
    edits 操作日志文件,记录了NameNode所要执行的操作。
    fstime 保存最近一次checkpoint的时间。
    当有写请求时,NameNode会首先写editlog到磁盘edits文件中,成功后才会修改内存,并向客户端返回。所以,fsimage中的数据并不是实时的数据,而是在达到条件时再进行更新,更新过程需要SecondaryNameNode参与NameNode的metadata信息会在启动后加载到内存中。
    为了防止在同一台服务器上启动多个namenode,会在namenode节点上多出一个dfs/name/in_use.lock文件。
    查看edits文件:
    hdfs oev -i edits_inprogress_0000000000000000001 -o edits.xml -p XML
    查看fsimage文件:
    hdfs oiv -i fsimage_0000000000000000008 -o aa.xml -p XML
  2. SecondaryNameNode:SecondaryNameNode并不是NameNode的热备份,而是协助者帮助NameNode进行元数据的合并,从另外的角度来看可以提供一定的备份功能(不能保证所有的数据都能恢复,只有在数据产生合并的时候才有这种性能),但并不是热备,这种合并过程可能会造成极端情况下数据丢失!可以从snn中恢复部分数据,但是无法恢复全部。
    何时触发数据合并?
    根据配置文件设置的时间间隔:fs.checkpoint.period 默认3600秒。
    根据配置文件设置的edits log大小 fs.checkpoint.size 默认64MB。
    当Hadoop被重启的时候,也会触发合并。
    合并过程
    Namenode元数据合并过程
    达到条件后 snn会将nn中的fsimage和edits文件通过网络拷贝过来,同时nn中会创建一个新的edits.new文件,新的读写请求会写入到这个edits.new中,在snn中将拷贝过来的fsimage和edits合并为一个新的fsimage,最后snn将合并完成的fsimage文件拷贝回nn中替换之前的fsimage,nn再将edtis.new改为edits。
    由于NameNode实时数据都在内存中,此处的合并指的是磁盘中的持久化的数据的处理。
    判断:snn可以对元数据做一定程度的备份,但是不是热备,对不对?
    思考:什么情况下可能造成NameNode元数据信息丢失?
    snn并不是nn的热备,但是能保存大部分备份数据。原因就在于edits.new中的数据丢失了就找不回来了。
    通常NameNode和SNN要放置到不同机器中以此提升性能,并提供一定的元数据安全性。
  3. DataNode:在hadoop中,数据是存放在DataNode上面的。是以Block的形式存储的。DataNode节点会不断向NameNode节点发送心跳报告。初始化时,每个数据节点将当前存储的数据块告知NameNode节点。通过向NameNode主动发送心跳保持与其联系(3秒一次)。后续DataNode节点在工作的过程中,数据节点仍会不断的更新NameNode节点与之对应的元数据信息,并接受来自NameNode节点的指令,创建、移动或者删除本地磁盘上的数据块。如果10分钟都没收到dn的心跳,则认为其已经lost,并copy其上的block到其他DataNode上。注意,在HDFS中,DataNode上存储的复本Replication是多复本策略。默认是三个。

MapReduce Shullfer过程以及调优

Shuffle过程图
Shuffle过程图
MapReduce Shullfer过程分为Map阶段和Reduce阶段,同时Shullfer调优也围绕着这两个阶段进行。

Map阶段:

  1. MapTask在接收到FileSplit之后进行按行读取。
  2. 每读取一行调用一次map方法。
  3. 执行完一次map之后会将输出的数据写到缓冲区中。
  4. 缓冲区的大小默认是100M,可以通过io.sort.mb来进行调节。
  5. 在缓冲区中,会对数据进行分区-partition,排序 - sort,合并 - combine操作。
  6. 当缓冲区的容量利用率达到阈值0.8的时候,会启动给一个后台线程将缓冲区中的数据写到指定目录下的溢写文件中,这个过程称之为是溢写 (Spill)。
  7. 每次的Spill都会产生一个新的溢写文件。
  8. 等最后所有的数据都写完之后,会将所有的溢写文件进行一次合并 (merge),合并到一个新的分区并且排序的文件中。
  9. 如果在最终合并的时候,溢写文件个数>=3,那么合并完成之后会再执行一次Combiner。

注意:

  1. 当产生溢写的时候,缓冲区最后残留的数据会flush到最后一个溢写文件中。
  2. Spill理论上默认是80M,但是要考虑序列化以及最后的冲刷等因素。
  3. 不能凭借一个MapTask处理的切片大小来衡量MapTask之后的输出数据的多少。
  4. 每一个切片对应一个MapTask,每一个MapTask对应一个缓冲区。
  5. 缓冲区本质上是一个字节数组。
  6. 缓冲区又叫环形缓冲区,好处在于可以重复利用同一块地址的缓冲区。
  7. 阈值的作用是避免Spill过程产生阻塞。
  8. merge过程可能不会发生。

Reduce阶段:

  1. ReduceTask通过Http的方式来得到输出文件的分区,这个过程称之为fetch。
  2. 每一个ReduceTask将获取的分区的数据再次进行merge,然后进行排序,将相同的key做聚合,将值放入迭代器中。
  3. 调用reduce方法,将key和迭代器传入。

注意:

  1. fetch的默认线程数是5,
  2. ReduceTask的阈值为5%,即当5%的MapTask完成之后,ReduceTask就开始启动。
  3. Merge因子默认为10,即每10个文件合并成一个文件。

Shuffle调优:
Map阶段:

  1. 调大缓冲区,一般可以调为250~350M。
  2. 可以引入combine过程。
  3. merge之后的文件可以进行压缩,减少网络传输的消耗。

Reduce阶段:

  1. 增多fetch的线程数。
  2. 降低ReduceTask的阈值。
  3. 提高merge因子。

Hive SQL查询速度较慢,如何优化

  1. map side join
    mapJoin的主要意思就是,当链接的两个表是一个比较小的表和一个特别大的表的时候,我们把比较小的table直接放到内存中去,然后再对比较大的表格进行map操作。
    join就发生在map操作的时候,每当扫描一个大的table中的数据,就要去去查看小表的数据,哪条与之相符,继而进行连接。
    这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle,在实际的应用中,我们这样设置:
    set hive.auto.convert.join=true;
    此外,hive有一个参数:hive.mapjoin.smalltable.filesize,默认值是25mb(其中一个表大小小于25mb时,自动启用mapjoin)要求:在hive做join时,要求小表在前(左)。
  2. join语句优化
    优化前:select m.cid,u.id form order m join customer u on m.cid=u.id where m.dt=’20160801’;
    优化后:select m.cid,u.id from (select cid from order where dt=’20160801’)m join customer u on m.cid = u.id;
    注意:Hive在做join时,小表写在前(左边)。
  3. group by 优化
    hive.groupby.skewindata=true如果group by过程出现倾斜,应该设置为true。
  4. count distinct 优化
    优化前:select count(distinct id )from tablename;
    优化后:select count(*) from (select distinct id from tablename)tmp;
    注意:count这种全局计数的操作,Hive只会用一个Reduce来实现。
    原因是Hive在处理COUNT这种“全聚合(full aggregates)”计算时,它会忽略用户指定的Reduce Task数,而强制使用1。
    所以我们只能采用变通的方法来绕过这一限制。
    我们利用Hive对嵌套语句的支持,将原来一个MapReduce作业转换为两个作业,在第一阶段选出全部的非重复id,在第二阶段再对这些已消重的id进行计数。
    这样在第一阶段我们可以通过增大Reduce的并发数,并发处理Map输出。
    在第二阶段,由于id已经消重,因此COUNT(*)操作在Map阶段不需要输出原id数据,只输出一个合并后的计数即可。
    这样即使第二阶段Hive强制指定一个Reduce Task,极少量的Map输出数据也不会使单一的Reduce Task成为瓶颈。

Java面试题

SpringMVC核心组件

  1. 前端控制器(DispatcherServlet):
    本质上是一个Servlet,相当于一个中转站,所有的访问都会走到这个Servlet中,再根据配置进行中转到相应的Handler(Controller)中进行处理,获取到数据和视图后,在使用相应视图做出响应。

  2. 处理器映射器(HandlerMapping):
    本质上就是一段映射关系,将访问路径和对应的Handler(Controller)存储为映射关系,在需要时供前端控制器查阅。(解析页面请求)

  3. 处理器适配器(HandlerAdapter):
    本质上是一个适配器,可以根据要求找到对应的Handler(Controller)来运行。前端控制器通过处理器映射器找到对应的Handler信息之后,将请求响应和对应的Handler信息交由处理器适配器处理,处理器适配器找到真正handler执行后,将结果即model和view返回给前端控制器

  4. 视图解析器(ViewResolver):
    本质上也是一种映射关系,可以将视图名称映射到真正的视图地址。前端控制器调用处理器适配完成后得到model和view,将view信息传给视图解析器得到真正的view。

SprintMVC请求响应流程图解:
SprintMVC请求响应流程图解

SpringMVC中的重定向和转发的实现

请求转发和重定向的区别:

“请求重定向”和“请求转发”都是web开发中资源跳转的方式。
请求转发是服务器内部的跳转:

  1. 地址栏不发生变化;
  2. 只有一个请求响应;
  3. 可以通过request域传递数据。

请求重定向是浏览器自动发起对跳转目标的请求:

  1. 地址栏会发生变化;
  2. 两次请求响应;
  3. 无法通过request域传递对象。

SpringMVC中实现转发和重定向:

  1. 在SpringMVC中仍然可以使用传统方式实现转发和重定向:
    request.getRequestDispatcher("").forward(request,response);
    response.sendRedirect("")

  2. 在SpringMVC中也提供了快捷方法实现转发和重定向,只要在返回时图时,使用如下方式指定即可:
    redirect:/xxx.action
    forward:/xxx.action

MyBatis中#{}与${}的差别(如何防止SQL注入)

  1. 在动态SQL解析阶段,#{}会被解析为JDBC预编译语句的参数标记符(占位符),例如上面的#{}语句将被解析为:
    select * from table_name where id=?;
  2. 而${}则底层使用Statement,字符串拼接,直接解析为字符串变量替换,当变量id的传参为"xiaoming"时,上面的${}语句将被解析为:
    select * from table_name where id='xiaoming';
    也就是说,对于变量替换,#{}发生在DBMS中,而${}发生在动态SQL解析阶段。
  3. #方式能够很大程度防止SQL注入,而$方式无法防止SQL注入。
  4. $方式一般用于传入数据库对象,例如传入表名。这是因为#{}解析的占位符在进行变量替换时,会带上单引号’ ',表名带单引号会导致SQL错误。
  5. 写在${}里面的内容必须有get方法。

数据库面试题

Python面试题

结语

更新于2019.03.24日,暂未完结,将持续更新。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值