大数据技术之高频面试题-专业技术基础

大数据技术之高频面试题
版本:V6.0 参考尚硅谷资料

文章目录


前言

本文为课程笔记,本着“只有亲身实践过并整理成体系才属于自己真正掌握的知识” 的理念写出本篇文章,后续每天更新,持续关注,欢迎留言讨论~。

第1章 面试说明

1.1 面试过程最关键的是什么?

1)不是你说了什么,而是你怎么说
2)大大方方的聊,放松
(体现技术自信,即使遇到不会的问题,大胆说没有学过,不要瞎编;有意识引导面试官问自己的会的知识)

1.2 面试时该怎么说?

1)语言表达清楚
(1)思维逻辑清晰表达流畅
(2)一二三层次表达
2)所述内容不犯错
(1)不说前东家或者自己坏话(这点就是我犯过的错,太诚实反而体现自己无知)
(2)往自己擅长的方面说
(3)实质,对考官来说,内容听过,就是自我肯定;没听过,那就是个学习的过程。
(4)基本的知识一定要背下来。但要用自己的理解去讲出来,不能让面试官认为你在背书。

1.3 面试技巧

1.3.1 六个常见问题
1)你的优点是什么?
大胆的说出自己各个方面的优势和特长,简练不啰嗦.
比如:热爱技术、写博客、帮助同学解决问题、帮助公司解决问题等、通过计算知识比如机器学习解决自己专业上面的问题(要准备好实际例子)、参加算法周赛、参加计算机相关比赛Kaggle、开发者大赛等(水赛就别说了)。
2)你的缺点是什么?
不要谈自己真实问题;用“缺点”衬托自己的优点
3)你的离职原因是什么?
不说前东家坏话,哪怕被伤过
合情合理合法
不要说超过1个以上的原因
4)您对薪资的期望是多少?
非终面不深谈薪资
只说区间,不说具体数字
底线是不低于当前薪资
非要具体数字,区间取中间值,或者当前薪资的+20%
5)您还有什么想问的问题?
这是体现个人眼界和层次的问题
问题本身不在于面试官想得到什么样的答案,而在于你跟别的应聘者的对比
标准答案:
公司希望我入职后的3-6个月内,给公司解决什么样的问题
公司(或者对这个部门)未来的战略规划是什么样子的?
以你现在对我的了解,您觉得我需要多长时间融入公司
6)您最快多长时间能入职?
一周左右,如果公司需要,可以适当提前
1.3.2 两个注意事项
1)职业化的语言
2)职业化的形象
1.3.3 自我介绍(控制在4分半以内,不超过5分钟)
1)个人基本信息
2)工作履历
时间、公司名称、任职岗位、主要工作内容、工作业绩、离职原因
3)深度沟通(也叫压力面试
刨根问底下沉式追问(注意是下沉式,而不是发散式的)
基本技巧:往自己熟悉的方向

第3章 项目架构

3.1 数仓概念

数据仓库的输入数据源和输出系统分别是什么?
输入系统:埋点产生的用户行为数据、JavaEE后台产生的业务数据
输出系统:报表系统用户画像系统推荐系统

[所谓埋点就是在应用中特定的流程收集一些信息,用来跟踪应用使用的状况,后续用来进一步优化产品或是提供运营的数据支撑,包括访问数(Visits),访客数(Visitor),停留时长(Time On Site),页面浏览数(Page Views)和跳出率(Bounce Rate)。这样的信息收集可以大致分为两种:页面统计(track this virtual page view),统计操作行为(track this button by an event)。 数据埋点的方式现在埋点的主流有两种方式:第一种:自己公司研发在产品中注入代码统计,并搭建起相应的后台查询。第二种:第三方统计工具,如友盟、神策、Talkingdata、GrowingIO等。]

3.2 系统数据流程设计

在这里插入图片描述

3.3 框架版本选型

1)Apache:运维麻烦,组件间兼容性需要自己调研。(一般大厂使用,技术实力雄厚,有专业的运维人员)
2)CDH:国内使用最多的版本,但CM不开源,但其实对中、小公司使用来说没有影响(建议使用)
3)HDP:开源,可以进行二次开发,但是没有CDH稳定,国内使用较少

3.4 服务器选型

服务器使用物理机还是云主机?
1)机器成本考虑:
(1)物理机:以128G内存,20核物理CPU,40线程,8THDD和2TSSD硬盘,单台报价4W出头,需考虑托管服务器费用。一般物理机寿命5年左右
(2)云主机,以阿里云为例,差不多相同配置,每年5W
2)运维成本考虑:
(1)物理机:需要有专业的运维人员
(2)云主机:很多运维工作都由阿里云已经完成,运维相对较轻松
3.5 集群规模(更改图片)
在这里插入图片描述
根据数据规模搭建集群(图换掉)

3.6 人员配置参考

3.6.1 整体架构

属于研发部,技术总监下面有各个项目组,我们属于数据组,其他还有后端项目组,基础平台等。总监上面就是副总等级别了。其他的还有产品运营部等。

3.6.2 你们部门的职级等级,晋升规则

职级就分初级,中级,高级。晋升规则不一定,看公司效益和职位空缺。

3.6.3 人员配置参考

小型公司(3人左右):组长1人,剩余组员无明确分工,并且可能兼顾javaEE和前端。
中小型公司(3~6人左右):组长1人,离线2人左右,实时1人左右(离线一般多于实时),JavaEE 1人(有或者没有人单独负责JavaEE,有时是有组员大数据和JavaEE一起做,或者大数据和前端一起做)。
中型公司(510人左右):组长1人,离线35人左右(离线处理、数仓),实时2人左右,JavaEE 1人左右(负责对接JavaEE业务),前端1人左右(有或者没有人单独负责前端)。
中大型公司(520人左右):组长1人,离线510人(离线处理、数仓),实时5人左右,JavaEE2人左右(负责对接JavaEE业务),前端1人(有或者没有人单独负责前端)。(发展比较良好的中大型公司可能大数据部门已经细化拆分,分成多个大数据组,分别负责不同业务)
上面只是参考配置,因为公司之间差异很大,例如ofo大数据部门只有5个人左右,因此根据所选公司规模确定一个合理范围,在面试前必须将这个人员配置考虑清楚,回答时要非常确定。

第4章 项目涉及技术

4.1 Linux&Shell相关总结

4.1.1 Linux常用命令

序号	命令	命令解释
1	top	查看内存
2	df -h	查看磁盘存储情况
3	iotop	查看磁盘IO读写(yum install iotop安装)
4	iotop -o	直接查看比较高的磁盘读写程序
5	netstat -tunlp | grep 端口号	查看端口占用情况
6	uptime	查看报告系统运行时长及平均负载
7	ps  aux	查看进程

4.1.2 Shell常用工具

awk、sed、cut、sort

4.2 Hadoop相关总结

4.2.1 Hadoop常用端口号

dfs.namenode.http-address:50070
dfs.datanode.http-address:50075
SecondaryNameNode辅助名称节点端口号:50090
dfs.datanode.address:50010
fs.defaultFS:8020 或者9000
yarn.resourcemanager.webapp.address:8088
历史服务器web访问端口:19888

4.2.2 Hadoop配置文件以及简单的Hadoop集群搭建

(1)配置文件:
core-site.xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml
hadoop-env.sh、yarn-env.sh、mapred-env.sh、slaves
(2)简单的集群搭建过程:

  1. JDK安装
  2. 配置SSH免密登录
  3. 配置hadoop核心文件:
  4. 格式化namenode

4.2.3 HDFS读流程和写流程

在这里插入图片描述

在这里插入图片描述

4.2.4 MapReduce的Shuffle过程及Hadoop优化(包括:压缩、小文件、集群优化)

在这里插入图片描述
在这里插入图片描述

一、Shuffle机制

1)Map方法之后Reduce方法之前这段处理过程叫Shuffle
2)Map方法之后,数据首先进入到分区方法,把数据标记好分区,然后把数据发送到环形缓冲区;环形缓冲区默认大小100m,环形缓冲区达到80%时,进行溢写;溢写前对数据进行排序,排序按照对key的索引进行字典顺序排序,排序的手段快排;溢写产生大量溢写文件,需要对溢写文件进行归并排序;对溢写的文件也可以进行Combiner操作,前提是汇总操作,求平均值不行。最后将文件按照分区存储到磁盘,等待Reduce端拉取。
3)每个Reduce拉取Map端对应分区的数据。拉取数据后先存储到内存中,内存不够了,再存储到磁盘。拉取完所有数据后,采用归并排序将内存和磁盘中的数据都进行排序。在进入Reduce方法前,可以对数据进行分组操作。

Mapreduces溢写
在这里插入图片描述

Map端溢写:

每个map task都有一个内存缓冲区,存储着map的输出结果,这个内存缓冲区是有大小限制的,默认是100MB。当map task的输出结果很多时,就可能会撑爆内存,所以需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill,中文可译为溢写(到磁盘)。当整个map task结束后再对磁盘中这个map task产生的所有临时文件做合并(因为最终的文件只有一个,所以需要将这些溢写文件归并到一起,这个过程就叫做Merge),生成最终的正式输出文件,然后等待reduce task来拉数据。
(这个溢写是由单独线程来完成,不影响往缓冲区写map结果的线程。溢写线程启动时不应该阻止map的结果输出,所以整个缓冲区有个溢写的比例spill.percent。这个比例默认是0.8,也就是当缓冲区的数据已经达到阈值(buffer size * spill percent = 100MB * 0.8 = 80MB),溢写线程启动,锁定这80MB的内存,执行溢写过程。Map task的输出结果还可以往剩下的20MB内存中写,互不影响。 )

mapper端Shuffle过程:

ps: partition是和sort一起做的,负责Spill的线程在拿到一段内存buf后会调用QuickSort的sort方法进行内存中的快排。mapper输出的keyvalue首先是按partition聚合。而我们如果指定key的compare方法会在这里生效并进行排序。 如果job没有定义combiner则直接写文件,如果有combiner则在这里进行combine。
在生成spill文件后还会将此次spillRecord的记录写在一个index文件中。当所有任务完成,就进入merge阶段。每个spill生成的文件中keyvalue都是有序的,但不同的溢写文件之间却是乱序的,类似多个有序文件的多路归并算法。Merger分别取出需要merge的spillfile的最小的keyvalue,放入一个内存堆中,每次从堆中取出一个最小的值,并把此值保存到merge的输出文件中。这里和hbase中scan的算法非常相似。这里merge时不同的partition的key是不会比较的,只有相同的partition的keyvalue才会进行排序和合并。如果用户定义了combiner,在merge的过程中也会进行combine,因为虽然第四步中combine过但那只是部分输入的combine,在merge时仍然需要combine。这里有人问了,既然这里有combiner,为啥在spill输出时还要combine纳,我认为是因为每次combine都会大大减少输出文件的大小,spill时就combine能减少一定的IO操作。在merge完后会把不同partition的信息保存进一个index文件以便之后reducer来拉自己部分的数据。 io.sort.mb的大小最好为 0.25 x mapred.child.java.opts(jvm可用内存) 可用内存至0.5 x mapred.child.java.opts之间。
在keyvalue对写入MapOutputBuffer时会调用partitioner.getPartition方法计算partition即应该分配到哪个reducer,这里的partition只是在内存的buf的index区写入一条记录。

Reduce端溢写:

reduce需要将map端的结果拉过来写入内存缓冲区中,当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程。

Reduce端Shuffle过程:
在这里插入图片描述

Mapreduce原理:

在map阶段,使用job.setInputFormatClass定义的InputFormat将输入的数据集分割成小数据块splites,同时InputFormat提供一个RecordReder的实现。本例子中使用的是TextInputFormat,他提供的RecordReder会将文本的一行的行号作为key,这一行的文本作为value。这就是自定义Map的输入是的原因。然后调用自定义Map的map方法,将一个个对输入给Map的map方法。注意输出应该符合自定义Map中定义的输出。最终是生成一个List。在map阶段的最后,会先调用job.setPartitionerClass对这个List进行分区,每个分区映射到一个reducer。每个分区内又调用job.setSortComparatorClass设置的key比较函数类排序。可以看到,这本身就是一个二次排序。如果没有通过job.setSortComparatorClass设置key比较函数类,则使用key的实现的compareTo方法。在第一个例子中,使用了IntPair实现的compareTo方法,而在下一个例子中,专门定义了key比较函数类。

在reduce阶段,reducer接收到所有映射到这个reducer的map输出后,也是会调用job.setSortComparatorClass设置的key比较函数类对所有数据对排序。然后开始构造一个key对应的value迭代器。这时就要用到分组,使用jobjob.setGroupingComparatorClass设置的分组函数类(多个map输出可能会到一个reduce)。只要这个比较器比较的两个key相同,他们就属于同一个组,它们的value放在一个value迭代器,而这个迭代器的key使用属于同一个组的所有key的第一个key。最后就是进入Reducer的reduce方法,reduce方法的输入是所有的(key和它的value迭代器)。同样注意输入与输出的类型必须与自定义的Reducer中声明的一致。

二、Hadoop优化

0)HDFS小文件影响

(1)影响NameNode的寿命,因为文件元数据存储在NameNode的内存中
(2)影响计算引擎的任务数量,比如每个小的文件都会生成一个Map任务

1)数据输入小文件处理:

(1)合并小文件:对小文件进行归档(Har)、自定义Inputformat将小文件存储成SequenceFile文件
(2)采用ConbinFileInputFormat来作为输入,解决输入端大量小文件场景。
(3)对于大量小文件Job,可以开启JVM重用

2)Map阶段

(1)增大环形缓冲区大小。由100m扩大到200m
(2)增大环形缓冲区溢写的比例。由80%扩大到90%
(3)减少对溢写文件的merge次数。
(4)不影响实际业务的前提下,采用Combiner提前合并,减少 I/O。

3)Reduce阶段

(1)合理设置Map和Reduce数:两个都不能设置太少,也不能设置太多。太少,会导致Task等待,延长处理时间太多,会导致 Map、Reduce任务间竞争资源,造成处理超时等错误。
(2)设置Map、Reduce共存:调整slowstart.completedmaps参数,使Map运行到一定程度后,Reduce也开始运行,减少Reduce的等待时间。
(3)规避使用Reduce,因为Reduce在用于连接数据集的时候将会产生大量的网络消耗
(4)增加每个Reduce去Map中拿数据的并行数
(5)集群性能可以的前提下,增大Reduce端存储数据内存的大小
4)IO传输
(1)采用数据压缩的方式,减少网络IO的的时间。安装Snappy和LZOP压缩编码器
(2)使用SequenceFile二进制文件
5)整体
(1)MapTask默认内存大小为1G,可以增加MapTask内存大小为4-5g
(2)ReduceTask默认内存大小为1G,可以增加ReduceTask内存大小为4-5g
(3)可以增加MapTask的cpu核数,增加ReduceTask的CPU核数
(4)增加每个Container的CPU核数和内存大小
(5)调整每个Map Task和Reduce Task最大重试次数
三、压缩

压缩格式		Hadoop自带?		算法		文件扩展名	支持切分	换成压缩格式后,原来的程序是否需要修改
DEFLATE		是,直接使用		DEFLATE	.deflate	否		和文本处理一样,不需要修改
Gzip		是,直接使用		DEFLATE	.gz			否		和文本处理一样,不需要修改
bzip2		是,直接使用		bzip2	.bz2		是		和文本处理一样,不需要修改
LZO			否,需要安装		LZO		.lzo		是		需要建索引,还需要指定输入格式
Snappy		否,需要安装		Snappy	.snappy		否		和文本处理一样,不需要修改

提示:如果面试过程问起,我们一般回答压缩方式为Snappy,特点速度快,缺点无法切分(可以回答在链式MR中,Reduce端输出使用bzip2压缩,以便后续的map任务对数据进行split

四、切片机制

1)简单地按照文件的内容长度进行切片
2)切片大小,默认等于Block大小
3)切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
提示:切片大小公式:max(0,min(Long_max,blockSize))

4.2.5 Yarn的Job提交流程

在这里插入图片描述
评分标准:5分

4.2.6 Yarn的默认调度器、调度器分类、以及他们之间的区别

1)Hadoop调度器重要分为三类:

FIFO 、Capacity Scheduler(容量调度器)和Fair Sceduler(公平调度器)。
Hadoop2.7.2默认的资源调度器是 容量调度器

2)区别:

FIFO调度器:先进先出,同一时间队列中只有一个任务在执行
在这里插入图片描述
容量调度器:多队列;每个队列内部先进先出,同一时间队列中只有一个任务在执行队列的并行度为队列的个数
在这里插入图片描述
公平调度器:多队列;每个队列内部按照缺额大小分配资源启动任务,同一时间队列中有多个任务执行队列的并行度大于等于队列的个数
在这里插入图片描述

3)一定要强调生产环境中不是使用的FIFOScheduler

面试的时侯会发现候选人大概了解这几种调度器的区别,但是问在生产环境用哪种,却说使用的FIFOScheduler(企业生产环境一定不会用这个调度的)

4.2.7 项目经验之LZO压缩

Hadoop默认不支持LZO压缩,如果需要支持LZO压缩,需要添加jar包,并在hadoop的cores-site.xml文件中添加相关压缩配置

4.2.8 Hadoop参数调优

1)在hdfs-site.xml文件中配置多目录,最好提前配置好,否则更改目录需要重新启动集群
2)NameNode有一个工作线程池,用来处理不同DataNode的并发心跳以及客户端并发的元数据操作。
dfs.namenode.handler.count=20 * log2(Cluster Size),比如集群规模为10台时,此参数设置为60
3)编辑日志存储路径dfs.namenode.edits.dir设置与镜像文件存储路径dfs.namenode.name.dir尽量分开,达到最低写入延迟
4)服务器节点上YARN可使用的物理内存总量,默认是8192(MB)8G,注意,如果你的节点内存资源不够8GB,则需要调减小这个值,而YARN不会智能的探测节点的物理内存总量。yarn.nodemanager.resource.memory-mb
5)单个任务可申请的最多物理内存量,默认是8192(MB)。yarn.scheduler.maximum-allocation-mb

4.2.9 项目经验之基准测试

搭建完Hadoop集群后需要对HDFS读写性能和MR计算能力测试。测试jar包在hadoop的share文件夹下

4.2.10 Hadoop宕机

1)如果MR造成系统宕机。此时要控制Yarn同时运行的任务数,和每个任务申请的最大内存。调整参数:yarn.scheduler.maximum-allocation-mb(单个任务可申请的最多物理内存量,默认是8192MB 8G
2)如果写入文件过量造成NameNode宕机。那么调高Kafka的存储大小,控制从Kafka到HDFS的写入速度。高峰期的时候用Kafka进行缓存,高峰期过去数据同步会自动跟上

4.3 Zookeeper相关总结

4.3.1 选举机制

半数机制

4.3.2 常用命令

ls、get、create

4.4 Flume相关总结

4.4.1 Flume组成,Put事务,Take事务

1)Flume组成,Put事务,Take事务
Taildir Source:断点续传、多目录。Flume1.6以前需要自己自定义Source记录每次读取文件位置,实现断点续传。
File Channel:数据存储在磁盘,宕机数据可以保存。但是传输速率慢。适合对数据传输可靠性要求高的场景,比如,金融行业。
Memory Channel:数据存储在内存中,宕机数据丢失。传输速率快。适合对数据传输可靠性要求不高的场景,比如,普通的日志数据。
Kafka Channel:减少了Flume的Sink阶段,提高了传输效率。

Source到Channel是Put事务
Channel到Sink是Take事务

在这里插入图片描述

4.4.2 Flume拦截器

(1)拦截器注意事项
	项目中自定义了:ETL拦截器和区分类型拦截器。

采用两个拦截器的优缺点:优点,模块化开发和可移植性;缺点,性能会低一些
(2)自定义拦截器步骤
a)实现 Interceptor
b)重写四个方法

  • initialize 初始化
  • public Event intercept(Event event) 处理单个Event
  • public List intercept(List events) 处理多个Event,在这个方法中调用Event intercept(Event event)
  • close 方法
    c)静态内部类,实现Interceptor.Builder

4.4.3 Flume Channel选择器

在这里插入图片描述

4.4.4 Flume监控器

Ganglia

4.4.5 Flume采集数据会丢失吗?(防止数据丢失的机制)

不会,Channel存储可以存储在File中,数据传输自身有事务

4.4.6 Flume内存

开发中在flume-env.sh中设置JVM heap为4G或更高,部署在单独的服务器上(4核8线程16G内存)
-Xmx与-Xms最好设置一致,减少内存抖动带来的性能影响,如果设置不一致容易导致频繁fullgc

4.4.7 FileChannel优化

通过配置dataDirs指向多个路径,每个路径对应不同的硬盘,增大Flume吞吐量。
官方说明如下:
Comma separated list of directories for storing log files. Using multiple directories on separate disks can improve file channel peformance
用逗号分隔的目录列表,用于存储日志文件。 在单独的磁盘上使用多个目录可以提高文件通道性能
checkpointDirbackupCheckpointDir也尽量配置在不同硬盘对应的目录中,保证checkpoint坏掉后,可以快速使用backupCheckpointDir恢复数据

4.4.8 HDFS Sink小文件处理

(1)HDFS存入大量小文件,有什么影响?
元数据层面:每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属组,权限,创建时间等,这些信息都保存在Namenode内存中。所以小文件过多,会占用Namenode服务器大量内存,影响Namenode性能和使用寿命
计算层面:默认情况下MR会对每个小文件启用一个Map任务计算,非常影响计算性能。同时也影响磁盘寻址时间
(2)HDFS小文件处理
官方默认的这三个参数配置写入HDFS后会产生小文件,hdfs.rollInterval、hdfs.rollSize、hdfs.rollCount
基于以上hdfs.rollInterval=3600,hdfs.rollSize=134217728,hdfs.rollCount =0,hdfs.roundValue=10,hdfs.roundUnit= second几个参数综合作用,效果如下:
(1)tmp文件在达到128M时会滚动生成正式文件
(2)tmp文件创建超10秒时会滚动生成正式文件

举例:在2018-01-01 05:23的时侯sink接收到数据,那会产生如下tmp文件:
/atguigu/20180101/atguigu.201801010520.tmp
即使文件内容没有达到128M,也会在05:33时滚动生成正式文件

4.5 Kafka相关总结

4.5.1 Kafka架构

在这里插入图片描述

4.5.2 Kafka压测

自带压力测试脚本(kafka-consumer-perf-test.shkafka-producer-perf-test.sh)。
Kafka压测时,可以查看到哪个地方出现了瓶颈(CPU,内存,网络IO)。一般都是网络IO达到瓶颈

4.5.3 Kafka的机器数量 =2*(峰值生产速度*副本数/100)+1

4.5.4 Kafka的日志保存时间 ——一周

4.5.5 Kafka的硬盘大小——一周*每天数据量

4.5.6 Kafka监控

公司自己开发的监控器;开源的监控器:KafkaManager、KafkaMonitor

4.5.7 Kakfa分区数

分区数并不是越多越好,一般分区数不要超过集群机器数量。分区数越多占用内存越大(ISR等),一个节点集中的分区也就越多,当它宕机的时候,对系统的影响也就越大。分区数一般设置为:3-10个

4.5.8 副本数设定

一般我们设置成2个或3个,很多企业设置为2个。

4.5.9 多少个Topic

通常情况:多少个日志类型就多少个Topic。也有对日志类型进行合并的。

4.5.10 Kafka丢不丢数据

Ack=0,相当于异步发送,消息发送完毕即offset增加,继续生产。
Ack=1,leader收到leader replica 对一个消息的接受ack才增加offset,然后继续生产。
Ack=-1,leader收到所有replica 对一个消息的接受ack才增加offset,然后继续生产。

4.5.11 Kafka的ISR副本同步队列

ISR(In-Sync Replicas),副本同步队列。ISR中包括Leader和Follower。如果Leader进程挂掉,会在ISR队列中选择一个服务作为新的Leader。有replica.lag.max.messages(延迟条数)和replica.lag.time.max.ms(延迟时间)两个参数决定一台服务是否可以加入ISR副本队列,在0.10版本移除了replica.lag.max.messages参数,防止服务频繁的进去队列。
任意一个维度超过阈值都会把Follower剔除出ISR,存入OSR(Outof-Sync Replicas)列表,新加入的Follower也会先存放在OSR中。

4.5.12 Kafka分区分配策略

在 Kafka内部存在两种默认的分区分配策略:Range和 RoundRobin
【Range】是默认策略

  • Range是对每个Topic而言的(即一个Topic一个Topic分),首先对同一个Topic里面的【分区】按照序号进行排序,并对【消费者】按照字母顺序进行排序。然后用Partitions分区的个数除以消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。
    例如:我们有10个分区,两个消费者(C1,C2),3个消费者线程,10 / 3 = 3而且除不尽。
    C1-0 将消费 0, 1, 2, 3 分区
    C2-0 将消费 4, 5, 6 分区
    C2-1 将消费 7, 8, 9 分区

【RoundRobin】

  • 前提:同一个Consumer Group里面的所有消费者的num.streams(消费者消费线程数)必须相等;
    每个消费者订阅的主题必须相同。
    第一步:将所有主题分区组成TopicAndPartition列表,然后对TopicAndPartition列表按照hashCode进行排序,最后按照轮询的方式发给每一个消费线程。

4.5.13 Kafka中数据量计算

每天总数据量100g,每天产生1亿条日志, 10000万/24/60/60=1150条/每秒钟
平均每秒钟:1150条
低谷每秒钟:400条
高峰每秒钟:1150条*(2-20倍)=2300条-23000条
每条日志大小:0.5k-2k
每秒多少数据量:2.3M-20MB

4.5.14 Kafka挂掉

1)Flume记录
2)日志有记录
3)短期没事

4.5.15 Kafka消息数据积压,Kafka消费能力不足怎么处理? (常考,考过)

1)如果是Kafka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量消费者数=分区数。(两者缺一不可)
2)如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。

4.6 Hive总结

4.6.1 Hive的架构

在这里插入图片描述

4.6.2 Hive和数据库比较

Hive 和数据库除了拥有类似的查询语言,再无类似之处。
1)数据存储位置
Hive 存储在 HDFS 。数据库将数据保存在块设备或者本地文件系统中。
2)数据更新
Hive中不建议对数据的改写。而数据库中的数据通常是需要经常进行修改的。
3)执行延迟
Hive 执行延迟较高。数据库的执行延迟较低。当然,这个是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优势。
4)数据规模
Hive支持很大规模的数据计算;数据库可以支持的数据规模较小。

4.6.3 内部表和外部表

1)管理表:当我们删除一个管理表时,Hive也会删除这个表中数据。管理表不适合和其他工具共享数据。
2)外部表:删除该表并不会删除掉原始数据,删除的是表的元数据

4.6.4 4个By区别

1)Sort By:分区内有序
2)Order By:全局排序,只有一个Reducer;
3)Distrbute By:类似MR中Partition,进行分区,结合sort by使用
4) Cluster By:当Distribute by和Sorts by字段相同时,可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外还兼具Sort by的功能。但是排序只能是升序排序不能指定排序规则为ASC或者DESC。

4.6.5 窗口函数

RANK() 排序相同时会重复,总数不会变
DENSE_RANK() 排序相同时会重复,总数会减少
ROW_NUMBER() 会根据顺序计算

1) OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化
2)CURRENT ROW:当前行
3)n PRECEDING:往前n行数据
4) n FOLLOWING:往后n行数据
5)UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING表示到后面的终点
6) LAG(col,n):往前第n行数据
7)LEAD(col,n):往后第n行数据
8) NTILE(n):把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。注意:n必须为int类型。

4.6.6 自定义UDF、UDTF

在项目中是否自定义过UDF、UDTF函数,以及用他们处理了什么问题,及自定义步骤?
1)自定义过。
2)用UDF函数解析公共字段;用UDTF函数解析事件字段
自定义UDF:继承UDF,重写evaluate方法
自定义UDTF:继承自GenericUDTF,重写3个方法:initialize(自定义输出的列名和类型),process(将结果返回forward(result)),close
为什么要自定义UDF/UDTF,因为自定义函数,可以自己埋点Log打印日志,出错或者数据异常,方便调试.

4.6.7 Hive优化

1)MapJoin
如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join。容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理
2)行列过滤
列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT
行处理:在分区剪裁中,当使用外关联时,如果将
副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤

3)采用分桶技术
4)采用分区技术
5)合理设置Map数
(1)通常情况下,作业会通过input的目录产生一个或者多个map任务。
主要的决定因素有:input的文件总个数input的文件大小集群设置的文件块大小
(2)是不是map数越多越好?
答案是
否定的*。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。
(3)是不是保证每个map处理接近128m的文件块,就高枕无忧了?
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。
针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;
6)小文件进行合并
在Map执行前合并小文件,减少Map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
7)合理设置Reduce数
Reduce个数并不是越多越好
(1)过多的启动和初始化Reduce也会消耗时间和资源
(2)另外,有多少个Reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;
在设置Reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的Reduce数使单个Reduce任务处理数据量大小要合适
8)常用参数
// 输出合并小文件
SET hive.merge.mapfiles = true; – 默认true,在map-only任务结束时合并小文件
SET hive.merge.mapredfiles = true; – 默认false,在map-reduce任务结束时合并小文件
SET hive.merge.size.per.task = 268435456; – 默认256M
SET hive.merge.smallfiles.avgsize = 16777216; – 当输出文件的平均大小小于该值时,启动一个独立的map-reduce任务进行文件merge

4.7 HBase总结

4.7.1 HBase存储结构

在这里插入图片描述

4.7.2 rowkey设计原则

1)rowkey长度原则
2)rowkey散列原则
3)rowkey唯一原则

4.7.3 RowKey如何设计

(1)生成随机数、hash、散列值
(2)字符串反转

4.7.4 Phoenix二级索引(讲原理)

4.8 Sqoop参数

/opt/module/sqoop/bin/sqoop import \
--connect \
--username \
--password \
--target-dir \
--delete-target-dir \
--num-mappers \
--fields-terminated-by   \
--query   "$2" ' and $CONDITIONS;'

4.8.1 Sqoop导入导出Null存储一致性问题

Hive中的Null在底层是以“\N”来存储,而MySQL中的Null在底层就是Null,为了保证数据两端的一致性。在导出数据时采用–input-null-string和–input-null-non-string两个参数。导入数据时采用–null-string和–null-non-string

4.8.2 Sqoop数据导出一致性问题

1)场景1:如Sqoop在导出到Mysql时,使用4个Map任务,过程中有2个任务失败,那此时MySQL中存储了另外两个Map任务导入的数据,此时老板正好看到了这个报表数据。而开发工程师发现任务失败后,会调试问题并最终将全部数据正确的导入MySQL,那后面老板再次看报表数据,发现本次看到的数据与之前的不一致,这在生产环境是不允许的。

官网:http://sqoop.apache.org/docs/1.4.6/SqoopUserGuide.html Since Sqoop
breaks down export process into multiple transactions, it is possible
that a failed export job may result in partial data being committed to
the database. This can further lead to subsequent jobs failing due to
insert collisions in some cases, or lead to duplicated data in others.
You can overcome this problem by specifying a staging table via the
–staging-table option which acts as an auxiliary table that is used to stage exported data. The staged data is finally moved to the
destination table in a single transaction. –staging-table方式 sqoop
export --connect jdbc:mysql://192.168.137.10:3306/user_behavior
–username root --password 123456 --table app_cource_study_report --columns watch_video_cnt,complete_video_cnt,dt --fields-terminated-by “\t” --export-dir
“/user/hive/warehouse/tmp.db/app_cource_study_analysis_${day}”
–staging-table app_cource_study_report_tmp --clear-staging-table --input-null-string ‘\N’

2)场景2:设置map数量为1个(不推荐,面试官想要的答案不只这个)
多个Map任务时,采用–staging-table方式,仍然可以解决数据一致性问题。

4.8.3 Sqoop底层运行的任务是什么

只有Map阶段,没有Reduce阶段的任务

4.8.4 Sqoop数据导出的时候一次执行多长时间

Sqoop任务5分钟-2个小时的都有。取决于数据量。

4.9 Scala

4.9.1 元组

1)元组的创建
val tuple1 = (1, 2, 3, “heiheihei”)
println(tuple1)
2)元组数据的访问,注意元组元素的访问有下划线,并且访问下标从1开始,而不是0
val value1 = tuple1._4
println(value1)
3)元组的遍历
方式1:

for (elem <- tuple1.productIterator  ) {
   print(elem)
}
println()

方式2:

tuple1.productIterator.foreach(i => println(i))
tuple1.produIterator.foreach(print(_))

4.9.2 隐式转换

隐式转换函数是以implicit关键字声明的带有单个参数的函数。这种函数将会自动应用,将值从一种类型转换为另一种类型。
implicit def a(d: Double) = d.toInt
//不加上边这句你试试
val i1: Int = 3.5
println(i1)

4.9.3 函数式编程理解

1)Scala中函数的地位:一等公民
2)Scala中的匿名函数(函数字面量)
3)Scala中的高阶函数
4)Scala中的闭包
5)Scala中的部分应用函数
6)Scala中的柯里化函数

4.9.4 样例类

case class Person(name:String,age:Int)
一般使用在 ds=df.as[Person]

4.9.5 柯里化

函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化,柯里化就是证明了函数只需要一个参数而已。其实我们刚的学习过程中,已经涉及到了柯里化操作,所以这也印证了,柯里化就是以函数为主体这种思想发展的必然产生的结果。
1)柯里化的示例

def mul(x: Int, y: Int) = x * y
println(mul(10, 10))
def mulCurry(x: Int) = (y: Int) => x * y
println(mulCurry(10)(9))
def mulCurry2(x: Int)(y:Int) = x * y
println(mulCurry2(10)(8))

2)柯里化的应用
比较两个字符串在忽略大小写的情况下是否相等,注意,这里是两个任务:
全部转大写(或小写)
比较是否相等
针对这两个操作,我们用一个函数去处理的思想,其实无意间也变成了两个函数处理的思想。示例如下:
val a = Array(“Hello”, “World”)
val b = Array(“hello”, “world”)
println(a.corresponds(b)(.equalsIgnoreCase()))
其中corresponds函数的源码如下:

def corresponds[B](that: GenSeq[B])(p: (A,B) => Boolean): Boolean = {
val i = this.iterator
val j = that.iterator
while (i.hasNext && j.hasNex  t)
  if (!p(i.next(), j.next()    ))
    return fals
!i.hasNext && !j.hasNext
}

尖叫提示:不要设立柯里化存在义这样的命题,柯里化,是面向函数思想的必然产生结果。

4.9.6 闭包

一个函数把外部的那些不属于自己的对象也包含(闭合)进来。
案例1:
def minusxy(x: Int) = (y: Int) => x - y
这就是一个闭包:

  1. 匿名函数(y: Int) => x -y嵌套在minusxy函数中。
  2. 匿名函数(y: Int) => x -y使用了该匿名函数之外的变量x
  3. 函数minusxy返回了引用了局部变量的匿名函数
    案例2
def minusxy(x: Int) = (y: Int) => x - y
val f1 = minusxy(10)
val f2 = minusxy(10)
println(f1(3) + f2(3))

此处f1,f2这两个函数就叫闭包。

4.9.7 Some、None、Option的正确使用

val map = Map("Tom"-> 23)
map("Jack") // 抛出异常 java.util.NoSuchElementException: key not found: Jack
map.get("Jack") // None
map("Tom") // 23
map.get("Tom") // Some(23)

使用模式匹配取出最后结果

val optionAge = map.get("Tom")
val age = optionAge match {
 case Some(x) => optionAge.get
 case None => 0
}

4.10 Spark

4.10.1 Spark有几种部署方式?请分别简要论述

1)Local:运行在一台机器上,通常是练手或者测试环境。
2)Standalone:构建一个基于Mster+Slaves的资源调度集群,Spark任务提交给Master运行。是Spark自身的一个调度系统。
3)Yarn: Spark客户端直接连接Yarn,不需要额外构建Spark集群。有yarn-client和yarn-cluster两种模式,主要区别在于:Driver程序的运行节点。
4)Mesos:国内大环境比较少用。

4.10.2 Spark任务使用什么进行提交,javaEE界面还是脚本

Shell 脚本。

4.10.3 Spark提交作业参数(重点)

参考答案:
https://blog.csdn.net/gamer_gyt/article/details/79135118
1)在提交任务时的几个重要参数

executor-cores —— 每个executor使用的内核数,默认为1,官方建议2-5个,我们企业是4个
num-executors —— 启动executors的数量,默认为2
executor-memory —— executor内存大小,默认1G
driver-cores —— driver使用内核数,默认为1
driver-memory —— driver内存大小,默认512M

2)边给一个提交任务的样式

spark-submit \
  --master local[5]  \
  --driver-cores 2   \
  --driver-memory 8g \
  --executor-cores 4 \
  --num-executors 10 \
  --executor-memory 8g \
  --class PackageName.ClassName XXXX.jar \
  --name "Spark Job Name" \
  InputPath      \
  OutputPath

4.10.4 简述Spark的架构与作业提交流程(画图讲解,注明各个部分的作用)(重点)

4.10.5 如何理解Spark中的血统概念(RDD)(笔试重点)

RDD在Lineage依赖方面分为两种Narrow Dependencies与Wide Dependencies用来解决数据容错时的高效性以及划分任务时候起到重要作用。

4.10.6 简述Spark的宽窄依赖,以及Spark如何划分stage,每个stage又根据什么决定task个数? (笔试重点)

Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。
Task:Stage是一个TaskSet,将Stage根据分区数划分成一个个的Task。

4.10.7 请列举Spark的transformation算子(不少于8个),并简述功能(重点)

1)map(func):返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成.
2)mapPartitions(func):类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。
3)reduceByKey(func,[numTask]):在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。
4)aggregateByKey (zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U: 在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
5)combineByKey(createCombiner: V=>C, mergeValue: (C, V) =>C, mergeCombiners: (C, C) =>C):
对相同K,把V合并成一个集合。
1.createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
2.mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
3.mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。

根据自身情况选择比较熟悉的算子加以介绍。

4.10.8 请列举Spark的action算子(不少于6个),并简述功能(重点)

1)reduce:
2)collect:
3)first:
4)take:
5)aggregate:
6)countByKey:
7)foreach:
8)saveAsTextFile:

4.10.9 请列举会引起Shuffle过程的Spark算子,并简述功能。

reduceBykey:
groupByKey:
…ByKey:

4.10.10 简述Spark的两种核心Shuffle

(HashShuffle与SortShuffle)的工作流程(包括未优化的HashShuffle、优化的HashShuffle、普通的SortShuffle与bypass的SortShuffle)(重点)
未经优化的HashShuffle:

优化后的Shuffle:

普通的SortShuffle:
当 shuffle read task 的 数 量 小 于 等 于 spark.shuffle.sort。
bypassMergeThreshold 参数的值时(默认为 200),就会启用 bypass 机制。

4.10.11 Spark常用算子reduceByKey与groupByKey的区别,哪一种更具优势?(重点)

reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v]。
groupByKey:按照key进行分组,直接进行shuffle。
开发指导:reduceByKey比groupByKey,建议使用。但是需要注意是否会影响业务逻辑。

4.10.12 Repartition和Coalesce关系与区别

1)关系:
两者都是用来改变RDD的partition数量的,repartition底层调用的就是coalesce方法:coalesce(numPartitions, shuffle = true)
2)区别:
repartition一定会发生shuffle,coalesce根据传入的参数来判断是否发生shuffle
一般情况下增大rdd的partition数量使用repartition,减少partition数量时使用coalesce

4.10.13 分别简述Spark中的缓存机制(cache和persist)与checkpoint机制,并指出两者的区别与联系

都是做RDD持久化的
cache:内存,不会截断血缘关系,使用计算过程中的数据缓存。
checkpoint:磁盘,截断血缘关系,在ck之前必须没有任何任务提交才会生效,ck过程会额外提交一次任务。

4.10.14 简述Spark中共享变量(广播变量和累加器)的基本原理与用途。(重点)

累加器(accumulator)是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。而广播变量用来高效分发较大的对象。
共享变量出现的原因:
通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。
Spark的两个共享变量,累加器与广播变量,分别为结果聚合与广播这两种常见的通信模式突破了这一限制。

4.10.15 当Spark涉及到数据库的操作时,如何减少Spark运行中的数据库连接数?

使用foreachPartition代替foreach,在foreachPartition内获取数据库的连接。

4.10.16 简述SparkSQL中RDD、DataFrame、DataSet三者的区别与联系? (笔试重点

1)RDD
优点:
编译时类型安全
编译时就能检查出类型错误
面向对象的编程风格
直接通过类名点的方式来操作数据
缺点:
序列化和反序列化的性能开销
无论是集群间的通信, 还是IO操作都需要对对象的结构和数据进行序列化和反序列化。
GC的性能开销,频繁的创建和销毁对象, 势必会增加GC
2)DataFrame
DataFrame引入了schema和off-heap
schema : RDD每一行的数据, 结构都是一样的,这个结构就存储在schema中。 Spark通过schema就能够读懂数据, 因此在通信和IO时就只需要序列化和反序列化数据, 而结构的部分就可以省略了。
3)DataSet
DataSet结合了RDD和DataFrame的优点,并带来的一个新的概念Encoder。
当序列化数据时,Encoder产生字节码与off-heap进行交互,能够达到按需访问数据的效果,而不用反序列化整个对象。Spark还没有提供自定义Encoder的API,但是未来会加入。
三者之间的转换:

4.10.17 SparkSQL中join操作与left join操作的区别?

join和sql中的inner join操作很相似,返回结果是前面一个集合和后面一个集合中匹配成功的,过滤掉关联不上的。
leftJoin类似于SQL中的左外关联left outer join,返回结果以第一个RDD为主,关联不上的记录为空。
部分场景下可以使用left semi join替代left join:
因为 left semi join 是 in(keySet) 的关系,遇到右表重复记录,左表会跳过,性能更高,而 left join 则会一直遍历。但是left semi join 中最后 select 的结果中只许出现左表中的列名,因为右表只有 join key 参与关联计算了

4.10.18 SparkStreaming有哪几种方式消费Kafka中的数据,它们之间的区别是什么?

一、基于Receiver的方式
这种方式使用Receiver来获取数据。Receiver是使用Kafka的高层次Consumer API来实现的。receiver从Kafka中获取的数据都是存储在Spark Executor的内存中的(如果突然数据暴增,大量batch堆积,很容易出现内存溢出的问题),然后Spark Streaming启动的job会去处理那些数据。
然而,在默认的配置下,这种方式可能会因为底层的失败而丢失数据。如果要启用高可靠机制,让数据零丢失,就必须启用Spark Streaming的预写日志机制(Write Ahead Log,WAL)。该机制会同步地将接收到的Kafka数据写入分布式文件系统(比如HDFS)上的预写日志中。所以,即使底层节点出现了失败,也可以使用预写日志中的数据进行恢复。
二、基于Direct的方式
这种新的不基于Receiver的直接方式,是在Spark 1.3中引入的,从而能够确保更加健壮的机制。替代掉使用Receiver来接收数据后,这种方式会周期性地查询Kafka,来获得每个topic+partition的最新的offset,从而定义每个batch的offset的范围。当处理数据的job启动时,就会使用Kafka的简单consumer api来获取Kafka指定offset范围的数据。
优点如下:
简化并行读取:如果要读取多个partition,不需要创建多个输入DStream然后对它们进行union操作。Spark会创建跟Kafka partition一样多的RDD partition,并且会并行从Kafka中读取数据。所以在Kafka partition和RDD partition之间,有一个一对一的映射关系。
高性能:如果要保证零数据丢失,在基于receiver的方式中,需要开启WAL机制。这种方式其实效率低下,因为数据实际上被复制了两份,Kafka自己本身就有高可靠的机制,会对数据复制一份,而这里又会复制一份到WAL中。而基于direct的方式,不依赖Receiver,不需要开启WAL机制,只要Kafka中作了数据的复制,那么就可以通过Kafka的副本进行恢复。
一次且仅一次的事务机制。
三、对比:
基于receiver的方式,是使用Kafka的高阶API来在ZooKeeper中保存消费过的offset的。这是消费Kafka数据的传统方式。这种方式配合着WAL机制可以保证数据零丢失的高可靠性,但是却无法保证数据被处理一次且仅一次,可能会处理两次。因为Spark和ZooKeeper之间可能是不同步的。
基于direct的方式,使用kafka的简单api,Spark Streaming自己就负责追踪消费的offset,并保存在checkpoint中。Spark自己一定是同步的,因此可以保证数据是消费一次且仅消费一次。
在实际生产环境中大都用Direct方式

4.10.19 简述SparkStreaming窗口函数的原理(重点)

窗口函数就是在原来定义的SparkStreaming计算批次大小的基础上再次进行封装,每次计算多个批次的数据,同时还需要传递一个滑动步长的参数,用来设置当次计算任务完成之后下一次从什么地方开始计算。
图中time1就是SparkStreaming计算批次大小,虚线框以及实线大框就是窗口的大小,必须为批次的整数倍。虚线框到大实线框的距离(相隔多少批次),就是滑动步长。

4.10.20 请手写出wordcount的Spark代码实现(Scala)(手写代码重点)

val conf: SparkConf = new SparkConf().setMaster(“local[*]”).setAppName(“WordCount”)

val sc = new SparkContext(conf)

sc.textFile("/input")
.flatMap(.split(" "))
.map((
,1))
.reduceByKey(+)
.saveAsTextFile("/output")
sc.stop()

4.10.21 如何使用Spark实现topN的获取(描述思路或使用伪代码)(重点)

方法1:
(1)按照key对数据进行聚合(groupByKey)
(2)将value转换为数组,利用scala的sortBy或者sortWith进行排序(mapValues)数据量太大,会OOM。
方法2:
(1)取出所有的key
(2)对key进行迭代,每次取出一个key利用spark的排序算子进行排序
方法3:
(1)自定义分区器,按照key进行分区,使不同的key进到不同的分区
(2)对每个分区运用spark的排序算子进行排序

4.10.22 京东:调优之前与调优之后性能的详细对比(例如调整map个数,map个数之前多少、之后多少,有什么提升)

这里举个例子。比如我们有几百个文件,会有几百个map出现,读取之后进行join操作,会非常的慢。这个时候我们可以进行coalesce操作,比如240个map,我们合成60个map,也就是窄依赖。这样再shuffle,过程产生的文件数会大大减少。提高join的时间性能。

第5章 用户行为数据分析

5.1 数仓分层架构表

分层优点:复杂问题简单化、清晰数据结构(方便管理)、增加数据的复用性、隔离原始数据(解耦)
ods 原始数据层 存放原始数据,保持原貌不做处理
dwd 明细数据层 对ods层数据清洗(去除空值,脏数据,超过极限范围的数据)
dws 服务数据层 轻度聚合
ads 应用数据层 具体需求
数仓中各层建的表都是外部表
5.2 埋点行为数据基本格式(基本字段)
公共字段:基本所有安卓手机都包含的字段
业务字段:埋点上报的字段,有具体的业务类型
下面就是一个示例,表示业务字段的上传。
行为数据启动日志/事件日志表关键字段:
{
“ap”:“xxxxx”,//项目数据来源 app pc
“cm”: { //公共字段
“mid”: “”, // (String) 设备唯一标识
“uid”: “”, // (String) 用户标识
“vc”: “1”, // (String) versionCode,程序版本号
“vn”: “1.0”, // (String) versionName,程序版本名
“l”: “zh”, // (String) 系统语言
“sr”: “”, // (String) 渠道号,应用从哪个渠道来的。
“os”: “7.1.1”, // (String) Android系统版本
“ar”: “CN”, // (String) 区域
“md”: “BBB100-1”, // (String) 手机型号
“ba”: “blackberry”, // (String) 手机品牌
“sv”: “V2.2.1”, // (String) sdkVersion
“g”: “”, // (String) gmail
“hw”: “1620x1080”, // (String) heightXwidth,屏幕宽高
“t”: “1506047606608”, // (String) 客户端日志产生时的时间
“nw”: “WIFI”, // (String) 网络模式
“ln”: 0, // (double) lng经度
“la”: 0 // (double) lat 纬度
},
“et”: [ //事件
{
“ett”: “1506047605364”, //客户端事件产生时间
“en”: “display”, //事件名称 启动和事件日志是根据事件名称的不同
“kv”: { //事件结果,以key-value形式自行定义
“goodsid”: “236”,
“action”: “1”,
“extend1”: “1”,
“place”: “2”,
“category”: “75”
}
}
]
}
根据事件标签的不同可以分成不同的日志表
5.3 项目经验总结
5.3.1 项目经验之元数据备份
元数据备份(重点,如数据损坏,可能整个集群无法运行,至少要保证每日零点之后备份到其它服务器两个复本)

5.3.2 日期处理函数
1)date_format函数(根据格式整理日期)
2)date_add、date_sub函数(加减日期)
3)next_day函数
4)last_day函数(求当月最后一天日期)
5)collect_set函数
6)get_json_object解析json函数
5.3.3 Union与Union all区别
1)union会将联合的结果集去重,效率较union all差
2)union all不会对结果集去重,所以效率高
5.3.4 Shell中单引号和双引号区别
1)在/home/atguigu/bin创建一个test.sh文件
[atguigu@hadoop102 bin]$ vim test.sh
在文件中添加如下内容
#!/bin/bash
do_date=$1

echo ‘ d o d a t e ′ e c h o " do_date' echo " dodateecho"do_date"
echo “' d o d a t e ′ " e c h o ′ " do_date'" echo '" dodate"echo"do_date”’
echo date
2)查看执行结果
[atguigu@hadoop102 bin]$ test.sh 2019-02-10
d o d a t e 2019 − 02 − 1 0 ′ 2019 − 02 − 1 0 ′ " do_date 2019-02-10 '2019-02-10' " dodate2019021020190210"do_date"
2019年 05月 02日 星期四 21:02:08 CST
3)总结:
(1)单引号不取变量值
(2)双引号取变量值
(3)反引号`,执行引号中命令
(4)双引号内部嵌套单引号,取出变量值
(5)单引号内部嵌套双引号,不取出变量值
5.3.5 Tez引擎优点?
Tez可以将多个有依赖的作业转换为一个作业,这样只需写一次HDFS,且中间节点较少,从而大大提升作业的计算性能。
5.4 ods层
1)ods_start_log 启动日志表
只有一个字段 line(保存着json),按照日期dt分区,表的格式:lzo
2)ods_event_log 事件日志表(格式同启动日志表)
只有一个字段 line ,按照日期dt 分区,表的格式:lzo
5.5 dwd层
1)dwd_start_log 启动表
关键字段:mid_id,user_id,dt(分区字段,按照日期分区) (其实这是启动表和事件表的公共字段)
从ods_start_log中的line用get_json_object(line,’ . m i d ′ ) m i d i d 的 方 式 获 取 字 段 5.5.1 自 定 义 U D F / U D T F ( 项 目 中 的 应 用 ) 自 定 义 U D F 函 数 ( 解 析 公 共 字 段 , 一 进 一 出 ) 自 定 义 U D T F 函 数 ( 解 析 具 体 事 件 字 段 , 一 进 多 出 ) 自 定 义 U D F : 继 承 U D F , 重 写 e v a l u a t e 方 法 自 定 义 U D T F : 继 承 自 G e n e r i c U D T F , 重 写 3 个 方 法 : i n i t i a l i z e ( 自 定 义 输 出 的 列 名 和 类 型 ) , p r o c e s s ( 将 结 果 返 回 f o r w a r d ( r e s u l t ) ) , c l o s e 为 什 么 要 自 定 义 U D F / U D T F , 因 为 自 定 义 函 数 , 可 以 自 己 埋 点 L o g 打 印 日 志 , 出 错 或 者 数 据 异 常 , 方 便 调 试 。 5.5.2 事 件 日 志 基 础 明 细 表 d w d b a s e e v e n t l o g 事 件 日 志 基 础 明 细 表 1 ) 关 键 字 段 : 公 共 字 段 : m i d i d , u s e r i d , d t ( 分 区 字 段 ) 以 及 e v e n t n a m e 、 e v e n t j s o n 、 s e r v e r t i m e 2 ) 从 o d s e v e n t l o g 的 l i n e 中 用 U D F 获 取 公 共 字 段 和 s e r v e r t i m e , 用 U D T F 获 取 e v e n t n a m e , e v e n t j s o n 。 5.5.3 商 品 点 击 表 d w d d i s p l a y l o g 商 品 点 击 表 关 键 字 段 : 公 共 字 段 + 特 有 字 段 从 d w d b a s e e v e n t l o g 中 直 接 获 取 公 共 字 段 和 s e r v e r t i m e , 从 d w d b a s e e v e n t l o g 的 e v e n t j s o n 中 获 取 特 有 字 段 , w h e r e e v e n t n a m e = " d i s p l a y " g e t j s o n o b j e c t ( e v e n t j s o n , ′ .mid') mid_id的方式获取字段 5.5.1 自定义UDF/UDTF(项目中的应用) 自定义UDF函数(解析公共字段,一进一出) 自定义UDTF函数(解析具体事件字段,一进多出) 自定义UDF:继承UDF,重写evaluate方法 自定义UDTF:继承自GenericUDTF,重写3个方法:initialize(自定义输出的列名和类型),process(将结果返回forward(result)),close 为什么要自定义UDF/UDTF,因为自定义函数,可以自己埋点Log打印日志,出错或者数据异常,方便调试。 5.5.2 事件日志基础明细表 dwd_base_event_log 事件日志基础明细表 1)关键字段: 公共字段:mid_id,user_id,dt(分区字段)以及event_name、event_json、server_time 2)从 ods_event_log的line 中用 UDF 获取 公共字段 和 server_time,用UDTF 获取 event_name , event_json 。 5.5.3 商品点击表 dwd_display_log 商品点击表 关键字段:公共字段 + 特有字段 从dwd_base_event_log中直接获取公共字段和server_time,从 dwd_base_event_log的 event_json中获取特有字段,where event_name = "display" get_json_object(event_json,' .mid)midid5.5.1UDF/UDTFUDFUDTFUDFUDFevaluateUDTFGenericUDTF3initialize()processforward(result)closeUDF/UDTFLog便5.5.2dwdbaseeventlog1mididuseriddt()eventnameeventjsonservertime2odseventloglineUDFservertimeUDTFeventname,eventjson5.5.3dwddisplaylog+dwdbaseeventlogservertimedwdbaseeventlogeventjsonwhereeventname="display"getjsonobject(eventjson,.kv.action’) action
5.5.4 其他的具体事件明细表
类似
dwd_newsdetail_log 商品详情页表
dwd_loading_log 商品列表页表
dwd_ad_log 广告表
dwd_notification_log 消息通知表
dwd_active_foreground_log 用户前台活跃表
dwd_active_background_log 用户后台活跃表
dwd_comment_log 评论表
dwd_favorites_log 收藏表
dwd_praise_log 点赞表
dwd_error_log 错误日志表
从一张事件基础明细表dwd_base_event_log一共可以获得11张具体事件明细表
5.6 需求一:用户活跃主题
5.6.1 DWS层日活明细表

5.6.2 DWS层周活明细表

5.6.3 DWS层月活明细表

5.6.4 ADS层日周月活跃设备数表

5.7 需求二:用户新增主题
5.7.1 DWS层日新增明细表

5.7.2 ADS层每日新增设备数表

5.8 需求三:用户留存主题

5.8.1 DWS层日留存明细表

5.8.2 ADS层留存用户数表

5.8.3 ADS层留存用户率表

5.9 需求四:沉默用户

5.10 需求五:本周回流用户数

5.11 需求六:流失用户数

5.12 需求七:最近连续3周活跃用户数

5.13 需求八:最近七天内连续三天活跃用户数

5.14 需求逻辑
5.14.1 如何分析用户活跃?
在启动日志中统计不同设备id出现次数。
5.14.2 如何分析用户新增?
用活跃用户表 left join 用户新增表,用户新增表中mid为空的即为用户新增。
5.14.3 如何分析用户1天留存?
留存用户=前一天新增 join 今天活跃
用户留存率=留存用户/前一天新增
5.14.4 如何分析沉默用户?
(登录时间为7天前,且只出现过一次)
按照设备id对日活表分组,登录次数为1,且是在一周前登录。
5.14.5 如何分析本周回流用户?
本周活跃left join本周新增 left join上周活跃,且本周新增id和上周活跃id都为null
5.14.6 如何分析流失用户?
(登录时间为7天前)
按照设备id对日活表分组,且七天内没有登录过。
5.14.7 如何分析最近连续3周活跃用户数?
按照设备id对周活进行分组,统计次数大于3次。
5.14.8 如何分析最近七天内连续三天活跃用户数?
1)查询出最近7天的活跃用户,并对用户活跃日期进行排名
2)计算用户活跃日期及排名之间的差值
3)对同用户及差值分组,统计差值个数
4)将差值相同个数大于等于3的数据取出,然后去重(去的是什么重???),即为连续3天及以上活跃的用户
第6章 业务交互数据分析
第三份文档(电商业务数据的分析和处理)
6.1 电商常识
SKU:一台银色、128G内存的、支持联通网络的iPhoneX
SPU:iPhoneX
Tm_id:品牌Id苹果,包括IPHONE,耳机,mac等
6.2 电商业务流程

6.3 业务表关键字段
6.3.1 订单表(order_info)
标签 含义
id 订单编号
total_amount 订单金额
order_status 订单状态
user_id 用户id
payment_way 支付方式
out_trade_no 支付流水号
create_time 创建时间
operate_time 操作时间
6.3.2 订单详情表(order_detail)
标签 含义
id 订单编号
order_id 订单号
user_id 用户id
sku_id 商品id
sku_name 商品名称
order_price 商品价格
sku_num 商品数量
create_time 创建时间
6.3.3 商品表
标签 含义
id skuId
spu_id spuid
price 价格
sku_name 商品名称
sku_desc 商品描述
weight 重量
tm_id 品牌id
category3_id 品类id
create_time 创建时间
6.3.4 用户表
标签 含义
id 用户id
name 姓名
birthday 生日
gender 性别
email 邮箱
user_level 用户等级
create_time 创建时间
6.3.5 商品一级分类表
标签 含义
id id
name 名称
6.3.6 商品二级分类表
标签 含义
id id
name 名称
category1_id 一级品类id
6.3.7 商品三级分类表
标签 含义
id id
name 名称
Category2_id 二级品类id
6.3.8 支付流水表
标签 含义
id 编号
out_trade_no 对外业务编号
order_id 订单编号
user_id 用户编号
alipay_trade_no 支付宝交易流水编号
total_amount 支付金额
subject 交易内容
payment_type 支付类型
payment_time 支付时间
订单表跟订单详情表有什么区别?
订单表的订单状态会变化,订单详情表不会,因为没有订单状态。
订单表记录user_id,订单id订单编号,订单的总金额order_status,支付方式,订单状态等。
订单详情表记录user_id,商品sku_id ,具体的商品信息(商品名称sku_name,价格order_price,数量sku_num)
6.4 MySql中表的分类
实体表,维度表,事务型事实表,周期性事实表
其实最终可以把事务型事实表,周期性事实表统称实体表,实体表,维度表统称维度表

订单表(order_info)(周期型事实表)
订单详情表(order_detail)(事务型事实表)
商品表(实体表)
用户表(实体表)
商品一级分类表(维度表)
商品二级分类表(维度表)
商品三级分类表(维度表)
支付流水表(事务型实体表)
6.5 同步策略

实体表,维度表统称维度表,每日全量或者每月(更长时间)全量
事务型事实表:每日增量
周期性事实表:拉链表
6.6 关系型数据库范式理论
1NF:属性不可再分割(例如不能存在5台电脑的属性,坏处:表都没法用)
2NF:不能存在部分函数依赖(例如主键(学号+课名)–>成绩,姓名,但学号–》姓名,所以姓名部分依赖于主键(学号+课名),所以要去除,坏处:数据冗余)
3NF:不能存在传递函数依赖(学号–》宿舍种类–》价钱,坏处:数据冗余和增删异常)
Mysql关系模型:关系模型主要应用与OLTP系统中,为了保证数据的一致性以及避免冗余,所以大部分业务系统的表都是遵循第三范式的。
Hive 维度模型:维度模型主要应用于OLAP系统中,因为关系模型虽然冗余少,
但是在大规模数据,跨表分析统计查询过程中,会造成多表关联,这会大大降低执行效率。
所以HIVE把相关各种表整理成两种:事实表和维度表两种。所有维度表围绕着事实表进行解释。
6.7 数据模型
雪花模型、星型模型和星座模型
(在维度建模的基础上又分为三种模型:星型模型、雪花模型、星座模型。)
星型模型(一级维度表),雪花(多级维度),星座模型(星型模型+多个事实表)
6.8 业务数据数仓搭建
sqoop
导数据的原理是mapreduce,
import 把数据从关系型数据库 导到 数据仓库,自定义InputFormat,
export 把数据从数据仓库 导到 关系型数据库,自定义OutputFormat,
用sqoop从mysql中将八张表的数据导入数仓的ods原始数据层
全量无条件,增量按照创建时间,增量+变化按照创建时间或操作时间。

origin_data
sku_info商品表(每日导全量)
user_info用户表(每日导全量)
base_category1商品一级分类表(每日导全量)
base_category2商品二级分类表(每日导全量)
base_category3商品三级分类表(每日导全量)
order_detail订单详情表(每日导增量)
payment_info支付流水表(每日导增量)
order_info订单表(每日导增量+变化)
6.8.1 ods层
(八张表,表名,字段跟mysql完全相同)
从origin_data把数据导入到ods层,表名在原表名前加ods_
6.8.2 dwd层
对ODS层数据进行判空过滤。对商品分类表进行维度退化(降维)。其他数据跟ods层一模一样
订单表 dwd_order_info
订单详情表 dwd_order_detail
用户表 dwd_user_info
支付流水表 dwd_payment_info
商品表 dwd_sku_info
其他表字段不变,唯独商品表,通过关联3张分类表,增加了
category2_idstring COMMENT '2id',category1_idstring COMMENT '3id',category3_namestring COMMENT '3',category2_namestring COMMENT '2',category1_name` string COMMENT ‘1’,
小结:
1)维度退化要付出什么代价?或者说会造成什么样的需求处理不了?
如果被退化的维度,还有其他业务表使用,退化后处理起来就麻烦些。
还有如果要删除数据,对应的维度可能也会被永久删除。
2)想想在实际业务中还有那些维度表可以退化
城市的三级分类(省、市、县)等
6.8.3 dws层

从订单表 dwd_order_info 中获取 下单次数 和 下单总金额
从支付流水表 dwd_payment_info 中获取 支付次数 和 支付总金额
从事件日志评论表 dwd_comment_log 中获取评论次数
最终按照user_id聚合,获得明细,跟之前的mid_id聚合不同
6.9 需求一:GMV成交总额

从用户行为宽表中dws_user_action,根据统计日期分组,聚合,直接sum就可以了。
6.10 需求二:转化率
6.10.1 新增用户占日活跃用户比率表

从日活跃数表 ads_uv_count 和 日新增设备数表 ads_new_mid_count 中取即可。
6.10.2 用户行为转化率表

从用户行为宽表dws_user_action中取,下单人数(只要下单次数>0),支付人数(只要支付次数>0)
从日活跃数表 ads_uv_count 中取活跃人数,然后对应的相除就可以了。
6.11 需求三:品牌复购率
需求:以月为单位统计,购买2次以上商品的用户
6.11.1 用户购买商品明细表(宽表)

6.11.2 品牌复购率表

从用户购买商品明细宽表dws_sale_detail_daycount中,根据品牌id–sku_tm_id聚合,计算每个品牌购买的总次数,购买人数a=购买次数>=1,两次及以上购买人数b=购买次数>=2,三次及以上购买人数c=购买次数>=3,
单次复购率=b/a,多次复购率=c/a
6.12 项目中有多少张宽表
宽表要3-5张,用户行为宽表,用户购买商品明细行为宽表,商品宽表,购物车宽表,物流宽表、登录注册、售后等。
1)为什么要建宽表
需求目标,把每个用户单日的行为聚合起来组成一张多列宽表,以便之后关联用户维度信息后进行,不同角度的统计分析。
6.13 拉链表

订单表拉链表 dwd_order_info_his
id string COMMENT ‘订单编号’,
total_amount decimal(10,2) COMMENT ‘订单金额’,
order_status string COMMENT ‘订单状态’,
user_id string COMMENT ‘用户id’ ,
payment_way string COMMENT ‘支付方式’,
out_trade_no string COMMENT ‘支付流水号’,
create_time string COMMENT ‘创建时间’,
operate_time string COMMENT ‘操作时间’ ,
start_date string COMMENT ‘有效开始日期’,
end_date string COMMENT ‘有效结束日期’
1)创建订单表拉链表,字段跟拉链表一样,只增加了有效开始日期和有效结束日期
初始日期,从订单变化表ods_order_info导入数据,且让有效开始时间=当前日期,有效结束日期=9999-99-99
(从mysql导入数仓的时候就只导了新增的和变化的数据ods_order_info,dwd_order_info跟ods_order_info基本一样,只多了一个id的判空处理)
2)建一张拉链临时表dwd_order_info_his_tmp,字段跟拉链表完全一致
3)新的拉链表中应该有这几部分数据,
(1)增加订单变化表dwd_order_info的全部数据
(2)更新旧的拉链表左关联订单变化表dwd_order_info,关联字段:订单id, where 过滤出end_date只等于9999-99-99的数据,如果旧的拉链表中的end_date不等于9999-99-99,说明已经是终态了,不需要再更新
如果dwd_order_info.id is null , 没关联上,说明数据状态没变,让end_date还等于旧的end_date
如果dwd_order_info.id is not null , 关联上了,说明数据状态变了,让end_date等于当前日期-1
把查询结果插入到拉链临时表中
4)把拉链临时表覆盖到旧的拉链表中
第7章 即席查询数据仓库

Kylin: T+1
Impala: CDH
Presto: Apache版本框架
第8章 项目中遇到过哪些问题
8.1 Hadoop宕机
(1)如果MR造成系统宕机。此时要控制Yarn同时运行的任务数,和每个任务申请的最大内存。调整参数:yarn.scheduler.maximum-allocation-mb(单个任务可申请的最多物理内存量,默认是8192MB)
(2)如果写入文件过量造成NameNode宕机。那么调高Kafka的存储大小,控制从Kafka到HDFS的写入速度。高峰期的时候用Kafka进行缓存,高峰期过去数据同步会自动跟上。
8.2 Ganglia监控
Ganglia监控Flume发现发现尝试提交的次数大于最终成功的次数
(1)增加Flume内存
(2)增加Flume台数
8.3 Flume小文件
Flume上传文件到HDFS时参数大量小文件?
调整hdfs.rollInterval、hdfs.rollSize、hdfs.rollCount这三个参数的值。
8.4 Kafka挂掉
(1)Flume记录
(2)日志有记录
(3)短期没事
8.5 Kafka消息数据积压,Kafka消费能力不足怎么处理?
(1)如果是Kafka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。(两者缺一不可)
(2)如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。
8.6 Kafka数据重复
在下一级消费者中去重。(redis、SparkStreaming)
8.7 Mysql高可用
Hive的metadata存储在MySql中(配置MySql的高可用(主从复制和读写分离和故障转移))
8.8 自定义UDF和UDTF解析和调试复杂字段
自定义UDF(extends UDF 实现evaluate方法) 解析公共字段
自定义UDTF(extends Genertic UDTF->实现三个方法init(指定返回值的名称和类型)、process(处理字段一进多出)、close方法) -> 更加灵活以及方便定义bug
8.9 Sqoop数据导出Parquet
Ads层数据用Sqoop往MySql中导入数据的时候,如果用了orc(Parquet)不能导入,需转化成text格式
8.10 Sqoop数据导出控制
Sqoop中导入导出Null存储一致性问题:
Hive中的Null在底层是以“\N”来存储,而MySQL中的Null在底层就是Null,为了保证数据两端的一致性。在导出数据时采用–input-null-string和–input-null-non-string两个参数。导入数据时采用–null-string和–null-non-string。
8.11 Sqoop数据导出一致性问题
当Sqoop导出数据到MySql时,使用4个map怎么保证数据的一致性
因为在导出数据的过程中map任务可能会失败,可以使用—staging-table –clear-staging
sqoop export --connect jdbc:mysql://192.168.137.10:3306/user_behavior --username root --password 123456 --table app_cource_study_report --columns watch_video_cnt,complete_video_cnt,dt --fields-terminated-by “\t” --export-dir “/user/hive/warehouse/tmp.db/app_cource_study_analysis_${day}” --staging-table app_cource_study_report_tmp --clear-staging-table --input-null-string ‘\N’
任务执行成功首先在tmp临时表中,然后将tmp表中的数据复制到目标表中(这个时候可以使用事务,保证事务的一致性)
8.12 SparkStreaming优雅关闭
如何优雅的关闭SparkStreaming任务(将写好的代码打包,Spark-Submit)
Kill -9 xxx ?
开启另外一个线程每5秒监听HDFS上一个文件是否存在。如果检测到存在,调用ssc.stop()方法关闭SparkStreaming任务(当你要关闭任务时,可以创建你自定义监控的文件目录)
8.13 Spark OOM、数据倾斜解决

第9章 项目经验
9.1 框架经验
9.1.1 Hadoop
1)Hadoop集群基准测试(HDFS的读写性能、MapReduce的计算能力测试)
2)一台服务器一般都有很多个硬盘插槽(插了几个插槽)
如果不配置datanode.data.dir多目录,每次插入一块新的硬盘都需要重启服务器
配置了即插即用
3)Hdfs参数调优
Namenode有一个工作线程池,用来处理与datanode的心跳(报告自身的健康状况和文件恢复请求)和元数据请求 dfs.namenode.handler.count=20 * log2(Cluster Size)
4)编辑日志存储路径dfs.namenode.edits.dir设置与镜像文件存储路径dfs.namenode.name.dir尽量分开,达到最低写入延迟(提高写入的吞吐量)
5)YARN参数调优yarn-site.xml
(1)服务器节点上YARN可使用的物理内存总量,默认是8192(MB)
(2)单个任务可申请的最多物理内存量,默认是8192(MB)。
6)HDFS和硬盘空闲控制在70%以下。
9.1.2 Flume
1)Flume内存配置为4G(flume-env.sh修改)
2)FileChannel优化
通过配置dataDirs指向多个路径,每个路径对应不同的硬盘,增大Flume吞吐量。
checkpointDir和backupCheckpointDir也尽量配置在不同硬盘对应的目录中,保证checkpoint坏掉后,可以快速使用backupCheckpointDir恢复数据
3)Sink:HDFS Sink小文件处理
这三个参数配置写入HDFS后会产生小文件,hdfs.rollInterval、hdfs.rollSize、hdfs.rollCount
9.1.3 Kafka
1)Kafka的吞吐量测试(测试生产速度和消费速度)
2)Kafka内存为6G(不能超过6G)
3)Kafka数量确定:2 * 峰值生产速度(m/s)* 副本数 / 100 + 1 = ?
4)Kafka中的数据量计算
每天数据总量100g(1亿条) 10000万/24/60/60 = 1150条/s
平均每秒钟:1150条
低谷每秒:400条
高峰每秒钟:1150 * 10 = 11000 条
每条日志大小: 1K左右
每秒多少数据量:20MB
5)Kafka消息数据积压,Kafka消费能力不足怎么处理?
(1)如果是Kafka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。(两者缺一不可)
(2)如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。
9.1.4 MySql之元数据备份
元数据备份(重点,如数据损坏,可能整个集群无法运行,至少要保证每日零点之后备份到其它服务器两个复本)

9.1.5 Tez引擎优点?
Tez可以将多个有依赖的作业转换为一个作业,这样只需写一次HDFS,且中间节点较少,从而大大提升作业的计算性能。
9.1.6 Sqoop参数
1)Sqoop导入导出Null存储一致性问题
2)Sqoop数据导出一致性问题
–staging-table方式 --clear-staging
3)Sqoop数据导出的时候一次执行多长时间
Sqoop任务5分钟-2个小时的都有。取决于数据量。
9.1.7 Azkaban每天执行多少个任务
(1)每天集群运行多少job?
(2)多个指标(200)6=1200(1000-2000个job)
(3)每天集群运行多少个task? 1000
(5-8)=5000多个
(4)任务挂了怎么办?运行成功或者失败都会发邮件
Zip a.job b.job c.job job.zip 把压缩的zip包放到azkaban的web界面上提交(指定sechduler)
9.2 业务经验
9.2.1 ODS层采用什么压缩方式和存储格式?
压缩采用Snappy,存储采用orc,压缩比是100g数据压缩完10g左右。
9.2.2 DWD层做了哪些事?
1)数据清洗
(1)空值去除
(2)过滤核心字段无意义的数据,比如订单表中订单id为null,支付表中支付id为空
(3)对手机号、身份证号等敏感数据脱敏
(4)对业务数据传过来的表进行维度退化和降维。
(5)将用户行为宽表和业务表进行数据一致性处理
select case when a is null then b else a end as JZR,

from A
2)清洗的手段
Sql、mr、rdd、kettle、Python(项目中采用sql进行清除)
3)清洗掉多少数据算合理
1万条数据清洗掉1条。
9.2.3 DWS层做了哪些事?
1)DWS层有3-5张宽表(处理100-200个指标 70%以上的需求)
具体宽表名称:用户行为宽表,用户购买商品明细行为宽表,商品宽表,购物车宽表,物流宽表、登录注册、售后等。
2)哪个宽表最宽?大概有多少个字段?
最宽的是用户行为宽表。大概有60-100个字段
3)具体用户行为宽表字段名称
评论、打赏、收藏、关注–商品、关注–人、点赞、分享、好价爆料、文章发布、活跃、签到、补签卡、幸运屋、礼品、金币、电商点击、gmv
CREATE TABLE app_usr_interact(
stat_dt date COMMENT ‘互动日期’,
user_id string COMMENT ‘用户id’,
nickname string COMMENT ‘用户昵称’,
register_date string COMMENT ‘注册日期’,
register_from string COMMENT ‘注册来源’,
remark string COMMENT ‘细分渠道’,
province string COMMENT ‘注册省份’,
pl_cnt bigint COMMENT ‘评论次数’,
ds_cnt bigint COMMENT ‘打赏次数’,
sc_add bigint COMMENT ‘添加收藏’,
sc_cancel bigint COMMENT ‘取消收藏’,
gzg_add bigint COMMENT ‘关注商品’,
gzg_cancel bigint COMMENT ‘取消关注商品’,
gzp_add bigint COMMENT ‘关注人’,
gzp_cancel bigint COMMENT ‘取消关注人’,
buzhi_cnt bigint COMMENT ‘点不值次数’,
zhi_cnt bigint COMMENT ‘点值次数’,
zan_cnt bigint COMMENT ‘点赞次数’,
share_cnts bigint COMMENT ‘分享次数’,
bl_cnt bigint COMMENT ‘爆料数’,
fb_cnt bigint COMMENT ‘好价发布数’,
online_cnt bigint COMMENT ‘活跃次数’,
checkin_cnt bigint COMMENT ‘签到次数’,
fix_checkin bigint COMMENT ‘补签次数’,
house_point bigint COMMENT ‘幸运屋金币抽奖次数’,
house_gold bigint COMMENT ‘幸运屋积分抽奖次数’,
pack_cnt bigint COMMENT ‘礼品兑换次数’,
gold_add bigint COMMENT ‘获取金币’,
gold_cancel bigint COMMENT ‘支出金币’,
surplus_gold bigint COMMENT ‘剩余金币’,
event bigint COMMENT ‘电商点击次数’,
gmv_amount bigint COMMENT ‘gmv’,
gmv_sales bigint COMMENT ‘订单数’)
PARTITIONED BY ( dt string)
5)商品详情 ----- 购物车 ----- 订单 ------ 付款的转换比率
5% 30% 70%
6)每天的GMV是多少,哪个商品卖的最好?每天下单量多少?
(1)100万的日活每天大概有10万人购买,平均每人消费100元,一天的GMV在1000万
(2)面膜,每天销售5000个
(3)每天下单量在10万左右
9.2.4 分析过哪些指标(一分钟至少说出30个指标)
1 离线指标
网站流量指标 独立访问数UV 页面访客数PV
流量质量指标类 跳出率 平均页面访问时长 人均页面访问数
2 购物车类指标 加入购物车次数 加入购物车买家次数 加入购物车商品数
购物车支付转化率
3 下单类指标 下单笔数 下单金额 下单买家数 浏览下单转化率
4 支付类指标 支付金额 支付买家数 支付商品数 浏览-支付买家转化率
下单-支付金额转化率 下单-支付买家数转换率
5 交易类指标 交易成功订单数 交易成功金额 交易成功买家数 交易成功商品数
交易失败订单数 交易失败订单金额 交易失败买家数
交易失败商品数 退款总订单量 退款金额 退款率
6 市场营销活动指标 新增访问人数 新增注册人数 广告投资回报率 UV订单转化率
7 风控类指标 买家评价数 买家上传图片数 买家评价率 买家好评率 买家差评率
物流平均配送时间
8 投诉类指标 发起投诉数 投诉率 撤销投诉(申诉数)
9 商品类指标 产品总数 SKU数 SPU数
上架商品SKU数 上架商品SPU数 上架商品数

日活跃用户,
月活跃用户,
各区域Top10商品统计,
季度商品品类点击率top10,
用户留存,
月APP的用户增长人数,
广告区域点击数top3,
活跃用户每天在线时长,
投诉人数占比,
沉默用户占比,
用户的新鲜度,
商品上架的sku数,
同种品类的交易额排名,
统计买家的评价率,
用户浏览时长,
统计下单的数量,
统计支付的数量,
统计退货的数量,
用户的(日活、月活、周活),
统计流失人数

日活,周活,月活,沉默用户占比,增长人数,活跃用户占比,在线时长统计,歌曲访问数,歌曲访问时长,各地区Top10歌曲统计 ,投诉人数占比,投诉回应时长,留存率,月留存率,转化率,GMV,复购vip率,vip人数,歌榜,挽回率,粉丝榜,打赏次数,打赏金额,发布歌曲榜单,歌曲热度榜单,歌手榜单,用户年龄组,vip年龄组占比,收藏数榜单,评论数
1.用户活跃数统计(日活,月活,周活)
2.某段时间的新增用户/活跃用户数
3.页面单跳转化率统计
4.活跃人数占比(占总用户比例)
5.在线时长统计(活跃用户每天在线时长)
12.统计本月的人均在线时长
6.订单产生效率(下单的次数与访问次数比)
7.页面访问时长(单个页面访问时长)
8.统计本季度付款订单
9.统计某广告的区城点击数top3
10.统计本月用户的流失人数
11.统计本月流失人数占用户人数的比例
13.统计本月APP的用户增长人数
14.统计本月的沉默用户
15.统计某时段的登录人数
16.统计本日用户登录的次数平均值
17.统计用户在某类型商品中的浏览深度(页面转跳率)
18.统计用户从下单开始到交易成功的平均时长
19.Top10热门商品的统计
20.统计下单的数量
21.统计支付的数量
22.统计退货的数量
23.统计动销率(有销量的商品/在线销售的宝贝)
24.统计支付转化率
25.统计用户的消费频率
26.统计商品上架的SKU数
27.统计同种品类的交易额排名
28.统计按下单退款排序的top10的商品
29.统计本APP的投诉人数占用户人数的比例
30.用户收藏商品
9.2.5 分析过最难的两个指标,现场手写

9.2.6 数据仓库每天跑多少张表,大概什么时候运行,运行多久?
基本一个项目建一个库,表格个数为初始的原始数据表格加上统计结果表格的总数。(一般70-100张表格)
每天0:30开始运行。
所有离线数据报表控制在8小时之内
大数据实时处理部分控制在5分钟之内。
评分标准:5分
9.2.7 数仓中使用的哪种文件存储格式
常用的包括:textFile,rcFile,ORC,Parquet,一般企业里使用ORC或者Parquet,因为是列式存储,且压缩比非常高,所以相比于textFile,查询速度快,占用硬盘空间少
9.2.8 数仓中用到过哪些Shell脚本及具体功能
1)集群启动停止脚本(Hadoop、Flume、Kafka、Zookeeper)
2)Sqoop和数仓之间的导入导出脚本
3)数仓层级之间的数据导入脚本。
9.2.9 项目中用过的报表工具
Echarts、kibana
9.2.10 测试相关
1)公司有多少台测试服务器?
测试服务器一般三台
2)测试数据哪来的?
一部分自己写Java程序自己造,一部分从生产环境上取一部分。
3)如何保证写的sql正确性
需要造一些特定的测试数据,测试。
离线数据和实时数据分析的结果比较。
4)测试环境什么样?
测试环境的配置是生产的一半
5)测试之后如何上线?
上线的时候,将脚本打包,提交git。先发邮件抄送经理和总监,运维。通过之后跟运维一起上线。
9.2.11 项目实际工作流程
1)先与产品讨论,看报表的各个数据从哪些埋点中取
2)将取得逻辑过程设计好,与产品确定后开始开发
3)开发出报表SQL脚本,并且跑几天的历史数据,观察结果
4)将报表放入调度任务中,第二天给产品看结果。
5)周期性将表结果导出或是导入后台数据库,生成可视化报表
9.2.12 项目中实现一个需求大概多长时间
刚入职第一个需求大概需要7天左右。
对业务熟悉后,平均一天一个需求。
影响时间的因素:开会讨论需求、表的权限申请、测试等
9.2.13 项目在3年内迭代次数,每一个项目具体是如何迭代的。
差不多一个月会迭代一次。就产品或我们提出优化需求,然后评估时间。每周我们都会开会做下周计划和本周总结。
有时候也会去预研一些新技术。
9.2.14 项目开发中每天做什么事
新需求比如埋点或是报表来了之后,需要设计做的方案,设计完成之后跟产品讨论,再开发。
数仓的任何步骤出现问题,需要查看问题,比如日活,月活下降等。
第10章 JavaSE(答案精简)
10.1 hashMap底层源码,数据结构
hashMap的底层结构在jdk1.7中由数组+链表实现,在jdk1.8中由数组+链表+红黑树实现,以数组+链表的结构为例。

JDK1.8之前Put方法:

JDK1.8之后Put方法:

10.2 Java自带有哪几种线程池?
1)newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种类型的线程池特点是:
工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
2)newFixedThreadPool
创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
3)newSingleThreadExecutor
创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
4)newScheduleThreadPool
创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。延迟3秒执行。
10.3 HashMap和HashTable区别
1)线程安全性不同
HashMap是线程不安全的,HashTable是线程安全的,其中的方法是Synchronize的,在多线程并发的情况下,可以直接使用HashTabl,但是使用HashMap时必须自己增加同步处理。
2)是否提供contains方法
HashMap只有containsValue和containsKey方法;HashTable有contains、containsKey和containsValue三个方法,其中contains和containsValue方法功能相同。
3)key和value是否允许null值
Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
4)数组初始化和扩容机制
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
10.4 TreeSet和HashSet区别
HashSet是采用hash表来实现的。其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法。
TreeSet是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是add()、remove()以及contains()等方法都是复杂度为O(log (n))的方法。它还提供了一些方法来处理排序的set,如first(), last(), headSet(), tailSet()等等。
10.5 String buffer和String build区别
1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,
2、只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低
10.6 Final、Finally、Finalize
final:修饰符(关键字)有三种用法:修饰类、变量和方法。修饰类时,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。修饰变量时,该变量使用中不被改变,必须在声明时给定初值,在引用中只能读取不可修改,即为常量。修饰方法时,也同样只能使用,不能在子类中被重写。
finally:通常放在try…catch的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
finalize:Object类中定义的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。
10.7 ==和Equals区别
== : 如果比较的是基本数据类型,那么比较的是变量的值
如果比较的是引用数据类型,那么比较的是地址值(两个对象是否指向同一块内存)
equals:如果没重写equals方法比较的是两个对象的地址值。
如果重写了equals方法后我们往往比较的是对象中的属性的内容

equals方法是从Object类中继承的,默认的实现就是使用==

第11章 Redis(答案精简)
11.1 缓存穿透、缓存雪崩、缓存击穿
1)缓存穿透是指查询一个一定不存在的数据。由于缓存命不中时会去查询数据库,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:
①是将空对象也缓存起来,并给它设置一个很短的过期时间,最长不超过5分钟
② 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
2)如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,就会造成缓存雪崩。
解决方案:
尽量让失效的时间点不分布在同一个时间点
3)缓存击穿,是指一个key非常热点,在不停的扛着大并发,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决方案:
可以设置key永不过期
11.2 哨兵模式
主从复制中反客为主的自动版,如果主机Down掉,哨兵会从从机中选择一台作为主机,并将它设置为其他从机的主机,而且如果原来的主机再次启动的话也会成为从机。
11.3 数据类型
string 字符串
list 可以重复的集合
set 不可以重复的集合
hash 类似于Map<String,String>
zset(sorted set) 带分数的set
11.4 持久化
1)RDB持久化:
①在指定的时间间隔内持久化
②服务shutdown会自动持久化
③ 输入bgsave也会持久化
2)AOF : 以日志形式记录每个更新操作
Redis重新启动时读取这个文件,重新执行新建、修改数据的命令恢复数据。
保存策略:
推荐(并且也是默认)的措施为每秒持久化一次,这种策略可以兼顾速度和安全性。
缺点:
1 比起RDB占用更多的磁盘空间
2 恢复备份速度要慢
3 每次读写都同步的话,有一定的性能压力
4 存在个别Bug,造成恢复不能
选择策略:
官方推荐:
如果对数据不敏感,可以选单独用RDB;不建议单独用AOF,因为可能出现Bug;如果只是做纯内存缓存,可以都不用
11.5 悲观锁
执行操作前假设当前的操作肯定(或有很大几率)会被打断(悲观)。基于这个假设,我们在做操作前就会把相关资源锁定,不允许自己执行期间有其他操作干扰。
11.6 乐观锁
执行操作前假设当前操作不会被打断(乐观)。基于这个假设,我们在做操作前不会锁定资源,万一发生了其他操作的干扰,那么本次操作将被放弃。Redis使用的就是乐观锁。
第12章 MySql
12.1 MyISAM与InnoDB的区别
对比项 MyISAM InnoDB
外键 不支持 支持
事务 不支持 支持
行表锁 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 行锁,操作时只锁某一行,不对其它行有影响,
适合高并发的操作
缓存 只缓存索引,不缓存真实数据 不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响

12.2 索引
数据结构:B+Tree
一般来说能够达到range就可以算是优化了
口诀(两个法则加6种索引失效的情况)
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写*;
不等空值还有OR,索引影响要注意;
VAR引号不可丢,SQL优化有诀窍。
12.3 b-tree和b+tree的区别

  1. B-树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的索引,记录只放在叶子节点中。
  2. 在B-树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。
    12.4 redis是单线程的,为什么那么快
    1)完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
    2)数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的
    3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
    4)使用多路I/O复用模型,非阻塞IO
    5)使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
    12.5 MySQL的事务
    一、事务的基本要素(ACID)
    1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位
    2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
    3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
    4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

二、事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

三、MySQL事务隔离级别
事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted) 是 是 是
不可重复读(read-committed) 否 是 是
可重复读(repeatable-read) 否 否 是
串行化(serializable) 否 否 否
12.6 常见面试sql
1.用一条SQL语句查询出每门课都大于80分的学生姓名
name kecheng fenshu
张三 语文 81
张三 数学 75
李四 语文 76
李四 数学 90
王五 语文 81
王五 数学 100
王五 英语 90

A: select distinct name from table where name not in (select distinct name from table where fenshu<=80)
B:select name from table group by name having min(fenshu)>80

  1. 学生表 如下:
    自动编号 学号 姓名 课程编号 课程名称 分数
    1 2005001 张三 0001 数学 69
    2 2005002 李四 0001 数学 89
    3 2005001 张三 0001 数学 69
    删除除了自动编号不同, 其他都相同的学生冗余信息

A: delete tablename where 自动编号 not in(select min(自动编号) from tablename group by学号, 姓名, 课程编号, 课程名称, 分数)

3.一个叫team的表,里面只有一个字段name,一共有4条纪录,分别是a,b,c,d,对应四个球队,现在四个球队进行比赛,用一条sql语句显示所有可能的比赛组合.

答:select a.name, b.name
from team a, team b
where a.name < b.name

4.面试题:怎么把这样一个
year month amount
1991 1 1.1
1991 2 1.2
1991 3 1.3
1991 4 1.4
1992 1 2.1
1992 2 2.2
1992 3 2.3
1992 4 2.4
查成这样一个结果
year m1 m2 m3 m4
1991 1.1 1.2 1.3 1.4
1992 2.1 2.2 2.3 2.4

答案
select year,
(select amount from aaa m where month=1 and m.year=aaa.year) as m1,
(select amount from aaa m where month=2 and m.year=aaa.year) as m2,
(select amount from aaa m where month=3 and m.year=aaa.year) as m3,
(select amount from aaa m where month=4 and m.year=aaa.year) as m4
from aaa group by year


5.说明:复制表(只复制结构,源表名:a新表名:b)

SQL: select * into b from a where 1<>1 (where1=1,拷贝表结构和数据内容)
ORACLE:create table b
As
Select * from a where 1=2

[<>(不等于)(SQL Server Compact)
比较两个表达式。 当使用此运算符比较非空表达式时,如果左操作数不等于右操作数,则结果为 TRUE。 否则,结果为 FALSE。]

原表:
courseid coursename score

1 java 70
2 oracle 90
3 xml 40
4 jsp 30
5 servlet 80

为了便于阅读,查询此表后的结果显式如下(及格分数为60):
courseid coursename score mark

1 java 70 pass
2 oracle 90 pass
3 xml 40 fail
4 jsp 30 fail
5 servlet 80 pass

写出此查询语句
select courseid, coursename ,score ,if(score>=60, “pass”,“fail”) as mark from course
7.表名:购物信息
购物人 商品名称 数量
A 甲 2
B 乙 4
C 丙 1
A 丁 2
B 丙 5
……

给出所有购入商品为两种或两种以上的购物人记录

答:select * from 购物信息 where 购物人 in (select 购物人 from 购物信息 group by 购物人 having count(*) >= 2);
8.
info 表
date result
2005-05-09 win
2005-05-09 lose
2005-05-09 lose
2005-05-09 lose
2005-05-10 win
2005-05-10 lose
2005-05-10 lose
如果要生成下列结果, 该如何写sql语句?
   win lose
2005-05-09 2 2
2005-05-10 1 2
答案:
(1) select date, sum(case when result = “win” then 1 else 0 end) as “win”, sum(case when result = “lose” then 1 else 0 end) as “lose” from info group by date;
(2) select a.date, a.result as win, b.result as lose
  from
  (select date, count(result) as result from info where result = “win” group by date) as a
  join
  (select date, count(result) as result from info where result = “lose” group by date) as b
  on a.date = b.date;

13.3 java中垃圾收集的方法有哪些?
1)引用计数法 应用于:微软的COM/ActionScrip3/Python等
a) 如果对象没有被引用,就会被回收,缺点:需要维护一个引用计算器
2)复制算法 年轻代中使用的是Minor GC,这种GC算法采用的是复制算法(Copying)
a) 效率高,缺点:需要内存容量大,比较耗内存
b) 使用在占空间比较小、刷新次数多的新生区
3)标记清除 老年代一般是由标记清除或者是标记清除与标记整理的混合实现
a) 效率比较低,会差生碎片。
4)标记压缩 老年代一般是由标记清除或者是标记清除与标记整理的混合实现
a) 效率低速度慢,需要移动对象,但不会产生碎片。
5)标记清除压缩标记清除-标记压缩的集合,多次GC后才Compact
a) 使用于占空间大刷新次数少的养老区,是3 4的集合体

第13章 JVM
13.1 JVM内存分哪几个区,每个区的作用是什么?

java虚拟机主要分为以下几个区:
1)方法区:
a.有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载
b.方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。
c.该区域是被线程共享的。
d.方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。
2)虚拟机栈:
a.虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
b.虚拟机栈是线程私有的,它的生命周期与线程相同。
c.局部变量表里存储的是基本数据类型、returnAddress类型(指向一条字节码指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定
d.操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式
e.每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用。
3)本地方法栈:
本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。
4)堆:
java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。
5)程序计数器:
内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区域是唯一一个java虚拟机规范没有规定任何OOM情况的区域。

13.2 Java类加载过程?
Java类加载需要经历一下几个过程:
1)加载
加载时类加载的第一个过程,在这个阶段,将完成一下三件事情:
a.通过一个类的全限定名获取该类的二进制流。
b.将该二进制流中的静态存储结构转化为方法去运行时数据结构。
c.在内存中生成该类的Class对象,作为该类的数据访问入口。
2)验证
验证的目的是为了确保Class文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证:
a.文件格式验证:验证字节流是否符合Class文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型.
b.元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。
c.字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。
d.符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。
e.准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
3)解析
该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。
4)初始化
初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。

13.4 如何判断一个对象是否存活?(或者GC对象的判定方法)
判断一个对象是否存活有两种方法:
1)引用计数法
2)可达性算法(引用链法)
13.5 什么是类加载器,类加载器有哪些?
实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。
主要有一下四种类加载器:
1)启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
2)扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
3)系统类加载器(system class loader)也叫应用类加载器:它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
4)用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

13.6 简述Java内存分配与回收策略以及Minor GC和Major GC(full GC)
内存分配:
1)栈区:栈分为java虚拟机栈和本地方法栈
2)堆区:堆被所有线程共享区域,在虚拟机启动时创建,唯一目的存放对象实例。堆区是gc的主要区域,通常情况下分为两个区块年轻代和年老代。更细一点年轻代又分为Eden区,主要放新创建对象,From survivor 和 To survivor 保存gc后幸存下的对象,默认情况下各自占比 8:1:1。
3)方法区:被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanment generation)
4)程序计数器:当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。线程私有的。

回收策略以及Minor GC和Major GC:
1)对象优先在堆的Eden区分配。
2)大对象直接进入老年代。
3)长期存活的对象将直接进入老年代。
当Eden区没有足够的空间进行分配时,虚拟机会执行一次Minor GC.Minor GC通常发生在新生代的Eden区,在这个区的对象生存期短,往往发生GC的频率较高,回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代GC的时候不会触发Minor GC,但是通过配置,可以在Full GC之前进行一次Minor GC这样可以加快老年代的回收速度。
第14章 JUC
14.1 Synchronized与Lock的区别
1)Synchronized能实现的功能Lock都可以实现,而且Lock比Synchronized更好用,更灵活。
2)Synchronized可以自动上锁和解锁;Lock需要手动上锁和解锁
14.2 Runnable和Callable的区别
1)Runnable接口中的方法没有返回值;Callable接口中的方法有返回值
2)Runnable接口中的方法没有抛出异常;Callable接口中的方法抛出了异常
3)Runnable接口中的落地方法是call方法;Callable接口中的落地方法是run方法
14.3 什么是分布式锁
当在分布式模型下,数据只有一份(或有限制),此时需要利用锁的技术控制某一时刻修改数据的进程数。分布式锁可以将标记存在内存,只是该内存不是某个进程分配的内存而是公共内存,如 Redis,通过set (key,value,nx,px,timeout)方法添加分布式锁。
14.4 什么是分布式事务
分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。
第15章 模拟考试
14.1 选择题
14.1.1 HDFS
1.下面哪个程序负责 HDFS 数据存储?
a)NameNode b)Jobtracker c)Datanode d)secondaryNameNode e)tasktracker
2. HDfS 中的 block 默认保存几份?
a)3份 b)2份 c)1份 d)不确定
3.下列哪个程序通常与NameNode 在一个节点启动?
a)SecondaryNameNode b)DataNode c)TaskTracker d)Jobtracker
解析:
JobTracker 对应于 NameNode
TaskTracker 对应于 DataNode
4. HDFS 默认 Block Size
a)32MB b)64MB c)128MB
注:旧版本是64MB
5. Client 端上传文件的时候下列哪项正确
a)数据经过 NameNode 传递给 DataNode
b)Client 端将文件切分为 Block,依次上传
c)Client 只上传数据到一台 DataNode,然后由 NameNode 负责 Block 复制工作
分析:
Client 向 NameNode 发起文件写入的请求。
NameNode 根据文件大小和文件块配置情况,返回给 Client 它所管理部分 DataNode 的信息。
Client 将文件划分为多个 Block,根据 DataNode 的地址信息,按顺序写入到每一个 DataNode 块中。
6.下面与 HDFS 类似的框架是?C
A NTFS B FAT32 C GFS D EXT3
14.1.2 集群管理
1.下列哪项通常是集群的最主要瓶颈 C
a)CPU b)网络 c)磁盘 IO d)内存
2.关于SecondaryNameNode 哪项是正确的? C
a)它是 NameNode 的热备
b)它对内存没有要求
c)它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间
d)SecondaryNameNode 应与 NameNode 部署到一个节点
3.配置机架感知的下面哪项正确 C
a)如果一个机架出问题,不会影响数据读写
b)写入数据的时候会写到不同机架的 DataNode 中
c)MapReduce 会根据机架获取离自己比较近的网络数据
4.下列哪个是 Hadoop 运行的模式 ABC
a)单机版 b)伪分布式 c)分布式
5. Cloudera 提供哪几种安装 CDH 的方法 A
a)Cloudera manager b)Tarball c)Yum d)Rpm
14.3.1 Zookeeper基础
1.下面与 Zookeeper 类似的框架是?D
AProtobuf
B Java
C Kafka
D Chubby
14.2 判断题
14.2.1 集群管理

  1. Ganglia 不仅可以进行监控,也可以进行告警。(正确)
  2. Nagios 不可以监控 Hadoop 集群,因为它不提供 Hadoop支持。(错误 )
    3.如果 NameNode 意外终止,SecondaryNameNode 会接替它使集群继续工作。(错误 )
  3. Cloudera CDH 是需要付费使用的。(错误)
  4. NameNode 负责管理 metadata,client 端每次读写请求,它都会从磁盘中读取或则会写入 metadata信息并反馈 client 端。(错误)
  5. DataNode 通过长连接与 NameNode 保持通信。错误
  6. Hadoop 自身具有严格的权限管理和安全措施保障集群正常运行。(错误)
  7. Slave 节点要存储数据,所以它的磁盘越大越好。(错误)
  8. hadoop dfsadmin –report 命令用于检测 HDFS 损坏块。(错误)
  9. Hadoop 默认调度器策略为 FIFO(错误 )
    11.集群内每个节点都应该配 RAID,这样避免单磁盘损坏,影响整个节点运行。(错误)
  10. Hadoop 环境变量中的 HADOOP_HEAPSIZE 用于设置所有 Hadoop 守护线程的内存。它默认是 200 GB。( 错误)
  11. DataNode 首次加入 cluster 的时候,如果 log 中报告不兼容文件版本,那需要 NameNode执行―Hadoopnamenode -format‖操作格式化磁盘。(错误 )
    14.2.2 HDFS
  12. Block Size 是不可以修改的。(错误)
  13. Hadoop 支持数据的随机读写。(错)
    3.因为 HDFS 有多个副本,所以 NameNode 是不存在单点问题的。(错误 )
    14.2.3 MapReduce
  14. Hadoop 是 Java 开发的,所以 MapReduce 只支持 Java 语言编写。(错误 )
  15. 每个 map就是一个线程。(错误)
  16. Mapreduce 的 input split 就是一个 block。(错误)

猜你喜欢

转载自blog.csdn.net/weixin_45091011/article/details/114157374