大数据开发实习准备记录10

这是在学习大数据各种组件之后,开始系统的刷sql题和面试题的第十天!

在牛客上选择SQL大厂面试真题系统刷题。

Sql:

1、2021年11月每天新用户的次日留存率
--留存率计算还是要通过左连接,左边新用户表,右边每天活跃用户表,左连接并限定活跃表的前一天时间和新用户表时间相等则留存下来,并在之后做count计算
select t1.dt,round(count(t2.uid)/count(t1.uid),2) uv_rate
from
(select uid,
min(date(in_time)) dt
from tb_user_log
group by uid)t1
left join
(select uid , date(in_time) dt
           from tb_user_log
           union
           select uid , date(out_time)
           from tb_user_log)t2 
on t1.uid=t2.uid and t1.dt=timestampadd(day,-1,t2.dt)   --date_sub(t2.dt,INTERVAL 1 day)
where date_format(t1.dt,'%Y-%m') = '2021-11'
group by t1.dt
order by t1.dt

2、统计活跃间隔对用户分级结果
--先分析需要用到的几个关键数据,用select先查询出来,再将其join,如果只是添加一列数据不用开窗函数的话可以用left join on 1
select user_grade,
round(count(uid)/max(user_cnt),2) ratio
from
(select uid,user_cnt,
case
    when last_dt_diff >= 30 then '流失用户'
    when last_dt_diff >=7 then '沉睡用户'
    when first_dt_diff < 7 then '新晋用户'
    else '忠实用户' end user_grade
from
(select uid,user_cnt,
timestampdiff(day,first_dt,cur_dt) first_dt_diff,
timestampdiff(day,last_dt,cur_dt) last_dt_diff
from(select uid,min(date(in_time)) first_dt,max(date(out_time)) last_dt
from tb_user_log group by uid )t1 
left join(select max(date(out_time)) cur_dt,count(distinct uid) user_cnt from tb_user_log
)t2 on 1)t3)t4
group by user_grade
order by ratio desc;

3、每天的日活数及新用户占比
--注意left join时用哪个表做左表,右表没匹配上的即为空值
--分析所需要的数据有哪些,先建立子查询
select dt,
count(uid) adu,
round(count(first_dt)/count(uid),2) uv_new_ratio
from
(select t1.uid,t1.dt,t2.first_dt from
(select uid,date(in_time) dt from tb_user_log
union
select uid,date(out_time) from tb_user_log)t1
left join
(select uid,min(date(in_time)) first_dt from tb_user_log 
group by uid)t2
on t1.uid=t2.uid and t1.dt = t2.first_dt)t3
group by dt
order by dt

4、连续签到领金币
--遇到连续签到领金币问题,分割为领多少金币和算连续问题
--连续:用开窗函数(用户分类,按日期排序),用日期减去对应排列序号,因为序号连续,那么日期减去排序序号为新的列,其中结果不变则为连续,再根据这新的相减之列做开窗排序(按用户和相减之列分类,按日期排序)则可以得到一列连续则递增排序,不连续则重新排序的列,再根据得金币规则,case when得到对应连续连续多少天得多少金币
select t2.uid,DATE_FORMAT(t2.dt,'%Y%m') month,sum(t2.coin) coin FROM
(select *,row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt) ranking2 ,
case 
when row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7=3 then 3
when row_number() over(PARTITION BY t1.uid,t1.dt2 order by t1.dt)%7=0 then 7
else 1 end coin 
FROM
(SELECT uid,date(in_time) dt,
row_number() over (partition by uid order by date(in_time)) ranking ,
date(in_time)-(row_number() over (partition by uid order by date(in_time))) dt2
FROM tb_user_log
where date(in_time) between '2021-07-07' and '2021-10-31'
and artical_id=0
and sign_in=1)t1)t2
group by t2.uid,DATE_FORMAT(t2.dt,'%Y%m')
order by t2.uid,DATE_FORMAT(t2.dt,'%Y%m')

面试题:

Hadoop

hadoop中常问的有三块,第一:存储,问到存储,就把HDFS相关的知识点拿出来;第二:计算框架(MapReduce);第三:资源调度框架(yarn)

1. 请说下HDFS读写流程

这个问题虽然见过无数次,面试官问过无数次,但是就是有人不能完整的说下来,所以请务必记住。并且很多问题都是从HDFS读写流程中引申出来的

HDFS写流程

宏观的:

1 客户端首先向NameNode发送请求上传文件,NameNode会做一些基础检查,例如:目标文件是否已存在,父目录是否存在等。

2 NameNode如果检查通过,则会返回该文件的所有Block块及对应存储的DataNode列表,否则,抛异常结束。

3 客户端收到Block块及对应存储的DataNode列表之后,就开始向对应的节点中上传数据。

4 客户端首先上传第一个Block块:blk-001,它对应的DataNode列表是bigdata01,bigdata02,bigdata03,所以客户端就把blk-001先上传到bigdata01,bigdata01再把blk-001上传到bigdata02,bigdata02再把blk-001上传到bigdata03.

5 客户端接着再上传第二个Block块:blk-002,流程如上。

6 当所有的Block块都传完以后,客户端会给NameNode返回一个状态信息,表示数据已经全部写入成功,或者失败。

7 NameNode根据客户端返回的状态信息来判断本次写入数据是成功还是失败,如果成功,则需要对应更新元数据信息。

底层的:

1 客户端通过调用DistributeFileSystem的create方法创建新文件

2 DistributeFileSystem通过RPC机制调用NameNode去创建一个没有Blocks关联的新文件。创建之前,NameNode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,NameNode就会记录下新文件,否则就会抛出IO异常。

3 前两步结束后会返回FSDataOutputStream对象,FSDataOutputSteam被封装成DFSOutputSream。DFSOutputStream可以协调NameNode和DataNode。客户端开始通过DFSOutputStream写数据,DFSOutputSteam会把数据切成一个个小Packet,然后排成队列(Data Quene)。

4 DataStream会去处理接收Data Queue,他先问询NameNode这个新的Block最适合存储的在哪几个DataNode里,比如副本数是3,那么就找到3个最适合的DataNode(会用到机架感知策略选择最适合的DataNode),把它们排成一个管道(pipeline)。DataStreame把Packet按队列输出到管道的第一个DataNode中,第一个DataNode又把Packet输出到第二个Datanode中,以此类推。

5 DFSOutputStream还有一个队列叫Ack Queue,也是由Packet组成,等待收到DataNode的响应,当Pipeline中的所有DataNode都表示已经收到的时候,这时Ack Queue才会把对应的Packet包移除掉。

6 客户端完成写数据后调用close方法关闭写入流

7 DataStreamer把剩余的Packet都刷写到Pipeline里然后等待Ack信息,收到最后一个Ack后,通知NameNode把文件标记为已完成。

注:Hadoop 在设计时考虑到数据的安全与高效, 数据文件默认在 HDFS 上存放三份, 存储策略为本地一份,同机架内其它某一节点上一份, 不同机架的某一节点上一份

解释:DFSClient向HDFS中写入输的过程中,有3个数据单位:Block、Packet和Chunk。

Block是最大的一个单位,它是最终存储与DataNode上的数据粒度,默认是128M。

Packet是中等的一个单位,它是数据由DFSClient流向DataNode的粒度,默认是64KB。

Chunk是最小的一个单位,它是DFSClient到DataNode数据传输中进行数据校验的粒度,默认是512B。

注意:事实上一个chunk还包含4B的校验值,因而Chunk写入Packet时是516B;数据与校验值的比值为128:1,所以对于一个128M的Block会有一个1M的校验文件与之对应。

Packet和Chunk之间的关系:

DFSClient向HDFS中写入数据的过程中会以Chunk、Packet和Data Quene3个粒度做3层缓存:

首先,当数据流入DFSOutputStream时,DFSOutputSteam内会有一个Chunk大小的buf(缓冲区),当数据写满这个buf(或遇到强制Flush时),会计算checksum值,然后写入Packet;

当一个Chunk写入Packet后,仍然不会立即发送,而是积累到一个Packet写满后,将这个Packet放入Data Quene队列中;

进入Data Queue队列的Packet会被另一线程按序取出发送到DataNode

HDFS读流程

宏观的:

  1. 客户端向NameNode请求下载指定文件,NameNode获取文件的元数据信息并且返回给客户端;

  2. 客户端根据返回的Block位置信息,请求对应的DataNode节点,获取数据;

  3. DataNode会向客户端传输数据,客户端会以Packet为单位接收,先在本地缓存,然后写入对应的文件;

1 首先调用FileSystem对象的open方法,其实是一个DistributedFileSystem的实例。

2 DistributedFileSystem通过RPC机制获得文件的第一个Block的Locations,同一个Block按照副本数会返回多个Locations,这些Locations按照Hadoop集群网络拓扑结构排序,距离客户端近的排在前面。

3 前两步会返回一个FSDataInputStream对象,该对象会被封装成DFSInputStream对象,DFSInputSteam可以方便的管理DataNode和NameNode数据流。客户端调用read方法,DFSInputStream会找出离客户端最近的DataNode并连接。

4 数据从DataNode源源不断的流向客户端。

5 如果第一块(Block)的数据读完了,就会关闭指向第一块的DataNode连接,接着读取下一块。这些操作对客户端来说是透明的,客户端的角度看来只是读一个持续不断的流。

6 如果第一批Block都读完了,DFSInputStream就会去NameNode获取下一批Blocks的Location,然后继续读,如果所有的块都读完了,这时就会关闭掉所有流。

注意:如果在读数据的时候,DFSInputSteam和DataNode的通讯发生异常,就会尝试正在读的Block的排第二近的DataNode,并且会记录哪个DataNode发生错误,剩余的Blocks读的时候就会直接跳过该DataNode。DFSInputStream也会检查Block数据校验和,如果发现一个坏的Block,就会先报告到NameNode节点,然后DFSInputStream在其它的DataNode上读该Block的镜像。

该设计的原则就是客户端直接连接DataNode来检索数据,NameNode来负责为每一个Block提供最优的DataNode,NameNode仅仅处理Block Location的请求,这些信息都加载在NameNode的内存中,HDFS通过DataNode可以承受大量客户端的并发访问。

2. HDFS在读取文件的时候,如果其中一个块突然损坏了怎么办

客户端读取完DataNode上的块之后会进行checksum 验证,也就是把客户端读取到本地的块与HDFS上的原始块进行校验,如果发现校验结果不一致,客户端会通知 NameNode,然后再从下一个拥有该 block 副本的DataNode 继续读

3. HDFS在上传文件的时候,如果其中一个DataNode突然挂掉了怎么办

客户端上传文件时与DataNode建立pipeline管道,管道正向是客户端向DataNode发送的数据包,管道反向是DataNode向客户端发送ack确认,也就是正确接收到数据包之后发送一个已确认接收到的应答,当DataNode突然挂掉了,客户端接收不到这个DataNode发送的ack确认,客户端会通知 NameNode,NameNode检查该块的副本与规定的不符,NameNode会通知DataNode去复制副本,并将挂掉的DataNode作下线处理,不再让它参与文件上传与下载。

4. NameNode在启动的时候会做哪些操作

NameNode数据存储在内存和本地磁盘,本地磁盘数据存储在fsimage镜像文件和edits编辑日志文件

首次启动NameNode

1、格式化文件系统,为了生成fsimage镜像文件

2、启动NameNode

(1)读取fsimage文件,将文件内容加载进内存

(2)等待DataNade注册与发送block report

3、启动DataNode

(1)向NameNode注册

(2)发送block report

(3)检查fsimage中记录的块的数量和block report中的块的总数是否相同

4、对文件系统进行操作(创建目录,上传文件,删除文件等)

(1)此时内存中已经有文件系统改变的信息,但是磁盘中没有文件系统改变的信息,此时会将这些改变信息写入edits文件中,edits文件中存储的是文件系统元数据改变的信息。

第二次启动NameNode

1、读取fsimage和edits文件

2、将fsimage和edits文件合并成新的fsimage文件

3、创建新的edits文件,内容为空

4、启动DataNode

5. Secondary NameNode了解吗,它的工作机制是怎样的

Secondary NameNode 是合并NameNode的edit logs到fsimage文件中;

它的具体工作机制:

(1)Secondary NameNode询问NameNode是否需要checkpoint。直接带回NameNode是否检查结果

(2)Secondary NameNode请求执行checkpoint

(3)NameNode滚动正在写的edits日志

(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode

(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并

(6)生成新的镜像文件fsimage.chkpoint

(7)拷贝fsimage.chkpoint到NameNode

(8)NameNode将fsimage.chkpoint重新命名成fsimage

所以如果NameNode中的元数据丢失,是可以从Secondary NameNode恢复一部分元数据信息的,但不是全部,因为NameNode正在写的edits日志还没有拷贝到Secondary NameNode,这部分恢复不了

6. Secondary NameNode不能恢复NameNode的全部数据,那如何保证NameNode数据存储安全

HDFS的HA,指的是在一个集群中存在多个NameNode,分别运行在独立的物理节点上。在任何时间点,只有一个NameNode是处于Active状态,其它的是处于Standby状态。 Active NameNode(简写为Active NN)负责所有的客户端的操作,而Standby NameNode(简写为Standby NN)用来同步Active NameNode的状态信息,以提供快速的故障恢复能力。

为了保证Active NN与Standby NN节点状态同步,即元数据保持一致。除了DataNode需要向这些NameNode发送block位置信息外,还构建了一组独立的守护进程”JournalNodes”(简写为JN),用来同步Edits信息。当Active NN执行任何有关命名空间的修改,它需要持久化到一半以上的JNs上。而Standby NN负责观察JNs的变化,读取从Active NN发送过来的Edits信息,并更新自己内部的命名空间。一旦Active NN遇到错误,Standby NN需要保证从JNs中读出了全部的Edits,然后切换成Active状态,如果有多个Standby NN,还会涉及到选主的操作,选择一个切换为Active 状态。

需要注意一点,为了保证Active NN与Standby NN节点状态同步,即元数据保持一致

这里的元数据包含两块,一个是静态的,一个是动态的

静态的是fsimage和edits,其实fsimage是由edits文件合并生成的,所以只需要保证edits文件内容的一致性。这个就是需要保证多个NameNode中edits文件内容的事务性同步。这块的工作是由JournalNodes集群进行同步的

动态数据是指block和DataNode节点的信息,这个如何保证呢? 当DataNode启动的时候,上报数据信息的时候需要向每个NameNode都上报一份。 这样就可以保证多个NameNode的元数据信息都一样了,当一个NameNode down掉以后,立刻从Standby NN中选择一个进行接管,没有影响,因为每个NameNode 的元数据时刻都是同步的。

注意:使用HA的时候,不能启动SecondaryNameNode,会出错。 之前是SecondaryNameNode负责合并edits到fsimage文件 那么现在这个工作被standby NN负责了。

NameNode 切换可以自动切换,也可以手工切换,如果想要实现自动切换,需要使用到zookeeper集群。

使用zookeeper集群自动切换的原理是这样的

当多个NameNode 启动的时候会向zookeeper中注册一个临时节点,当NameNode挂掉的时候,这个临时节点也就消失了,这属于zookeeper的特性,这个时候,zookeeper就会有一个watcher监视器监视到,就知道这个节点down掉了,然后会选择一个节点转为Active,把down掉的节点转为Standby。

如果面试官再问HA中的 共享存储 是怎么实现的知道吗?

可以进行解释下:NameNode 共享存储方案有很多,比如 Linux HA, VMware FT, QJM等,目前社区已经把由 Clouderea 公司实现的基于 QJM(Quorum Journal Manager)的方案合并到 HDFS 的 trunk 之中并且作为默认的共享存储实现

基于 QJM 的共享存储系统主要用于保存 EditLog,并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。QJM 共享存储的基本思想来自于 Paxos 算法,采用多个称为 JournalNode 的节点组成的 JournalNode 集群来存储 EditLog。每个 JournalNode 保存同样的 EditLog 副本。每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数 (majority) 的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉

7. 在NameNode HA中,会出现脑裂问题吗?怎么解决脑裂

假设 NameNode1 当前为 Active 状态,NameNode2 当前为 Standby 状态。如果某一时刻 NameNode1 对应的 ZKFailoverController 进程发生了“假死”现象,那么 Zookeeper 服务端会认为 NameNode1 挂掉了,根据前面的主备切换逻辑,NameNode2 会替代 NameNode1 进入 Active 状态。但是此时 NameNode1 可能仍然处于 Active 状态正常运行,这样 NameNode1 和 NameNode2 都处于 Active 状态,都可以对外提供服务。这种情况称为脑裂

脑裂对于NameNode 这类对数据一致性要求非常高的系统来说是灾难性的,数据会发生错乱且无法恢复。Zookeeper 社区对这种问题的解决方法叫做 fencing,中文翻译为隔离,也就是想办法把旧的 Active NameNode 隔离起来,使它不能正常对外提供服务。

在进行 fencing 的时候,会执行以下的操作:

首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态。

如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施,通常会选择 sshfence:

(1) sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死

(2) shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离

8. 小文件过多会有什么危害,如何避免

针对HDFS而言,每一个小文件在namenode中都会占用150字节的内存空间,最终会导致集群中虽然存储了很多个文件,但是文件的体积并不大,这样就没有意义了。

针对MapReduce而言,每一个小文件都是一个Block,都会产生一个InputSplit,最终每一个小文件都会产生一个map任务,这样会导致同时启动太多的Map任务,Map任务的启动是非常消耗性能的,但是启动了以后执行了很短时间就停止了,因为小文件的数据量太小了,这样就会造成任务执行消耗的时间还没有启动任务消耗的时间多,这样也会影响MapReduce执行的效率。

针对这个问题,解决办法通常是选择一个容器,将这些小文件组织起来统一存储,HDFS提供了两种类型的容器,分别是SequenceFile 和 MapFile

SequeceFile是Hadoop 提供的一种二进制文件,这种二进制文件直接将对序列化到文件中。 一般对小文件可以使用这种文件合并,即将小文件的文件名作为key,文件内容作为value序列化到大文件中 但是这个文件有一个缺点,就是它需要一个合并文件的过程,最终合并的文件会比较大,并且合并后的文件查看起来不方便,必须通过遍历才能查看里面的每一个小文件 所以这个SequenceFile 其实可以理解为把很多小文件压缩成一个大的压缩包了。

MapFile是排序后的SequenceFile,MapFile由两部分组成,分别是index和data
index作为文件的数据索引,主要记录了每个Record的key值,以及该Record在文件中的偏移位置。在MapFile被访问的时候,索引文件会被加载到内存,通过索引映射关系可迅速定位到指定Record所在文件位置,因此,相对SequenceFile而言,MapFile的检索效率是高效的,缺点是会消耗一部分内存来存储index数据

9. 请说下HDFS的组织架构

1)Client:客户端

(1)切分文件。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行存储

(2)与NameNode交互,获取文件的位置信息

(3)与DataNode交互,读取或者写入数据

(4)Client提供一些命令来管理HDFS,比如启动关闭HDFS、访问HDFS目录及内容等

2)NameNode:名称节点,也称主节点,存储数据的元数据信息,不存储具体的数据

(1)管理HDFS的名称空间

(2)管理数据块(Block)映射信息

(3)配置副本策略

(4)处理客户端读写请求

3)DataNode:数据节点,也称从节点。NameNode下达命令,DataNode执行实际的操作

(1)存储实际的数据块

(2)执行数据块的读/写操作

4)Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务

(1)辅助NameNode,分担其工作量

(2)定期合并Fsimage和Edits,并推送给NameNode

(3)在紧急情况下,可辅助恢复NameNode

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值