因作者近期正在投递数据开发岗位,所以会在此记录一些面试过程中的问题,持续更新,直到入职新公司为止
1. 数仓建模的三范式理论
所谓的范式,就是我们在关系建模的时候所遵从的一些规范,而三范式,指的就是三条规范
1.1 优点与缺点
优点:
- 十几年前,磁盘很贵,为了减少磁盘存储
- 以前没有分布式系统,都是单机,只能增加磁盘,磁盘个数也是有限的
- 一次修改,需要修改多个表,很难保证数据一致性 即使是在当下,第三条仍然是一大优点
缺点:
- 会产生很多张表,导致在获取数据时,需要通过Join拼接出最后的数据
1.2 三范式
1.2.1 第一范式
第一范式1NF核心原则就是:属性不可切。
这里所谓的属性,就是我们表中的字段,字段内容是不可切的,字段必须是原子性的,我们举个例子。
上面这张图就不符合第一范式,因为商品字段仍然可以进行切分,可以将【2台电脑】放在两个字段里存储
这个时候,就会说已经满足了第一范式(1NF)
1.2.2 第二范式
第二范式2NF核心原则就是:不能存在部分依赖
在下面这个例子里,就不符合第二范式,因为【订单号】和【商品ID】作为联合主键,【实付金额】字段会由【订单号】和【商品ID】共同确定,但【订单下单日期】却和【商品ID】字段没有关系,由【订单号】字段就能确定,所以【订单下单日期】就存在部份依赖,这时候就不符合第二范式
而处理的方法也很简单,只需要在当前表里去掉【订单下单日期】,新建一张表,存放【订单号】和【订单下单日期】就可以了,在使用的时候,可以通过【订单号】进行关联
1.2.3 第三范式
第三范式3NF核心原则:不能存在传递依赖
在下面这个例子里,就不符合第三范式,可以看到【订单下单时间】是【订单号】的时间属性,【店铺ID】也是可以直接通过【订单号】查询得到,但【店铺所在地区】却不是通过【订单号】直接获取,而是由【店铺ID】获得的,所以这里就存在传递依赖,因此不符合第三范式
解决方式也很简单,拆成两张表就可以了
1.3. 三范式总结
其实这3个规范条件,说白了就是在确定表里的主键,定好唯一维度,表里也不存其他的信息,字段都是和主键直接有关的内容,也正是因为这样,所以会需要很多张表,才能满足复杂的业务需求
2. mapreduce的执行过程和hdfs的读写流程
2.1 mapreduce的执行过程
- Input:数据输入以及数据分片,定义如何从HDFS等存储系统中输入数据
- Map:对每个数据片进行数据处理,并将处理后的数据按照业务代码规则输出(分操作,将任务分成多个小任务执行)
- Shuffle:对Map的输出数据进行分区、排序、分组、combiner(非必需)操作,供Reduce获取
- Reduce:获取Map的输出数据,将获取的数据合并聚合,并将聚合结果输出(合操作,将map阶段的输出进行合并)
- Output:定义如何将Reduce的输出值输出到HDFS等存储系统中
2.2 hdfs的读写流程
2.2.1 读
- Client向NameNode发送RPC请求。请求文件block的位置
- NameNode收到请求之后会检查用户权限以及是否有这个文件,如果都符合,则会视情况返回部分或全部的block列表,对于每个block,NameNode都会返回含有该block副本的DataNode地址;这些返回的DataNode地址,会按照集群拓扑结构得出DataNode与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离 Client 近的排靠前;心跳机制中超时汇报的DataNode状态为STALE,这样的排靠后
- Client选取排序靠前的DataNode来读取block,如果客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性)
- 底层上本质是建立Socket Stream(FSDataInputStream),重复的调用父类DataInputStream的read方法,直到这个块上的数据读取完毕
- 当读完列表的block后,若文件读取还没有结束,客户端会继续向NameNode 获取下一批的block列表
- 读取完一个block都会进行checksum验证,如果读取DataNode时出现错误,客户端会通知NameNode,然后再从下一个拥有该block副本的DataNode 继续读
- read方法是并行的读取block信息,不是一块一块的读取;NameNode只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据
- 最终读取来所有的block会合并成一个完整的最终文件
2.2.2 写
- Client客户端发送上传请求,通过RPC与NameNode建立通信,NameNode检查该用户是否有上传权限,以及上传的文件是否在HDFS对应的目录下重名,如果这两者有任意一个不满足,则直接报错,如果两者都满足,则返回给客户端一个可以上传的信息
- Client根据文件的大小进行切分,默认128M一块,切分完成之后给NameNode发送请求第一个block块上传到哪些服务器上
- NameNode收到请求之后,根据网络拓扑和机架感知以及副本机制进行文件分配,返回可用的DataNode的地址
- 客户端收到地址之后与服务器地址列表中的一个节点如A进行通信,本质上就是RPC调用,建立pipeline,A收到请求后会继续调用B,B在调用C,将整个pipeline建立完成,逐级返回Client
- Client开始向A上发送第一个block(先从磁盘读取数据然后放到本地内存缓存),以packet(数据包,64kb)为单位,A收到一个packet就会发送给B,然后B发送给C,A每传完一个packet就会放入一个应答队列等待应答
- 数据被分割成一个个的packet数据包在pipeline上依次传输,在pipeline反向传输中,逐个发送ack(命令正确应答),最终由pipeline中第一个DataNode节点A将pipelineack发送给Client
- 当一个block传输完成之后, Client再次请求NameNode上传第二个block,NameNode重新选择三台DataNode给Client。
2.2.3 读写流程总结
说白了,就是客户端、NAMENODE、DATANODE三者之间的联动,里面涉及的通信手段过于专业了
3. 猎头提供-面试题
3.1 离线数仓部分
3.1.1 数仓建模理论
3.1.1.1 当前公司数仓分层是如何划分的?每一层都是用来做什么的?
一般分为4层,
1.贴源层 ODS:数据与业务系统保持一致
2.明细层 DWD:数据明细层对ods层进行数据清洗, 标准化,数据仍满足3NF模型(三范式),为分析运算做准备
3.数据汇总层 DWS:按照分析主题进行汇总,存储类型非3NF,注重数据聚合,复杂查询,处理性能更优的数仓模型,如维度模型。
4.数据应用层 ADS :用来存储查询结果,数据应用层也称为数据集市,对接外部系统,方便前端业务快读查询
3.1.1.2 为什么要进行数仓分层,分层有什么好处?
1.清晰数据结构:方便数据血缘追踪。
2.把复杂问题简单化:将一个复杂的任务分解成多个步骤来完成,每一层只处理单一的部分,比较简单和容易理解
3.减少重复开发:规范数据分层,开发一些通用的中间层数据,能够减少极大的重复计算
4.屏蔽原始数据的异常:屏蔽业务的影响,不必改一次业务就需要重新接入数据
3.1.1.3 建模方法,三范式+维度建模有什么区别
范式建模:
优点:能够结合业务系统的数据模型,较方便的实现数据仓库的模型;同一份数据只存放在一个地方,没有数据冗余,保证了数据一致性;数据解耦,方便维护。
缺点:表的数量多;查询时关联表较多使得查询性能降低。
维度建模:
优点:模型结构简单,面向分析,为了提高查询性能可以增加数据冗余,反规范化的设计,开发周期短,能够快速迭代。
缺点:数据会大量冗余,预处理阶段开销大,后期维护麻烦;还有一个问题就是不能保证数据口径一致性
3.1.1.4 星型模型和雪花模型的区别
星型模型:当所有维表都直接连接到事实表上时,整个图解就像星星一样
雪花模型:当有一个或多个维表没有直接连接到事实表上,而是通过其他维度表连接到事实表上时,其图解就像多个雪花连接在一起
3.1.1.5 维度建模的步骤和过程是什么样的?如何根据一个业务主题进行建模
维度建模一般按照以下四个步骤:
选择业务过程→声明粒度→确认维度→确认事实
1.选择业务过程:
在业务系统中,挑选我们感兴趣的业务线,比如下单业务,支付业务,退款业务,物流业务,一条业务线对应一张事实表。
2.声明粒度:
数据粒度指数据仓库的数据中保存数据的细化程度或综合程度的级别。声明粒度意味着精确定义事实表中的一行数据表示什么,应该尽可能选择最小粒度,以此来应各种各样的需求。
典型的粒度声明如下:
订单事实表中一行数据表示的是一个订单中的一个商品项。
支付事实表中一行数据表示的是一个支付记录。
3.确定维度:
维度的主要作用是描述业务是事实,主要表示的是“谁,何处,何时”等信息。
确定维度的原则是:后续需求中是否要分析相关维度的指标。例如,需要统计,什么时间下的订单多,哪个地区下的订单多,哪个用户下的订单多。需要确定的维度就包括:时间维度、地区维度、用户维度。
4.确定事实:
此处的“事实”一词,指的是业务中的度量值(次数、个数、件数、金额,可以进行累加),例如订单金额、下单次数等。
在DWD层,以业务过程为建模驱动,基于每个具体业务过程的特点,构建最细粒度的明细层事实表。事实表可做适当的宽表化处理。
事实表和维度表的关联比较灵活,但是为了应对更复杂的业务需求,可以将能关联上的表尽量关联上。
3.1.1.6 事实表的类型以及他们的作用
事实表中的每行数据代表一个业务事件(下单、支付、退款、评价等)。“事实”这个术语表示的是业务事件的度量值(可统计次数、个数、金额等)
1.事务型事实表:
以每个事务或事件为单位,例如一个销售订单记录,一笔支付记录等,作为事实表里的一行数据。一旦事务被提交,事实表数据被插入,数据就不再进行更改,其更新方式为增量更新
2.周期型快照事实表:
周期型快照事实表中不会保留所有数据,只保留固定时间间隔的数据,例如每天或者每月的销售额,或每月的账户余额等
3.累积型快照事实表:
累计快照事实表用于跟踪业务事实的变化。例如,数据仓库中可能需要累积或者存储订单从下订单开始,到订单商品被打包、运输、和签收的各个业务阶段的时间点数据来跟踪订单声明周期的进展情况。当这个业务过程进行时,事实表的记录也要不断更新
3.1.1.7 维度退化是什么意思
当一个维度没有数据仓库需要的任何数据的时候就可以退化此维度,需要把退化的相关数据迁移到事实表中,然后删除退化的维度。退化维度没有对应的维表,但可以获取与之相关的事实
退化维度优点:
1.减少事实表和维度表的关联
2.该技术减少维度的数量, 简化维度数据仓库模式。 简单的模式比复杂的更容易理解, 也有更好的查询性能。
3.如果存在退化维,ETL的过程会变得非常容易
4.可以让group by 等操作变得更快
3.1.1.8 拉链表的含义以及作用
拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息
优点:(1)保留了数据的历史信息;(2)节省存储空间;
3.1.2 大数据组件部分
3.1.2.1 OLTP和OLAP的区别
OLTP(Online transaction processing):在线/联机事务处理。典型的OLTP类操作都比较简单,主要是对数据库中的数据进行增删改查,操作主体一般是产品的用户。
OLAP(Online analytical processing):指联机分析处理。通过分析数据库中的数据来得出一些结论性的东西。比如给老总们看的报表,用于进行市场开拓的用户行为统计,不同维度的汇总分析结果等等。操作主体一般是运营、销售和市场等团队人员。
单次OLTP处理的数据量比较小,所涉及的表非常有限,一般仅一两张表。而OLAP是为了从大量的数据中找出某种规律性的东西,经常用到count()、sum()和avg()等聚合方法,用于了解现状并为将来的计划/决策提供数据支撑,所以对多张表的数据进行连接汇总非常普遍。
3.1.2.2 Hive的MR过程?
- Input:数据输入以及数据分片,定义如何从HDFS等存储系统中输入数据
- Map:对每个数据片进行数据处理,并将处理后的数据按照业务代码规则输出(分操作,将任务分成多个小任务执行)
- Shuffle:对Map的输出数据进行分区、排序、分组、combiner(非必需)操作,供Reduce获取
- Reduce:获取Map的输出数据,将获取的数据合并聚合,并将聚合结果输出(合操作,将map阶段的输出进行合并)
- Output:定义如何将Reduce的输出值输出到HDFS等存储系统中
3.1.2.3 在MR的过程中会发生几次排序?
在MapReduce的shuffle阶段共有三次排序,分别是:
1.Map的溢写(spill)阶段:根据分区及key进行快速排序;
2.Map合并溢写文件阶段:将同一个分区的多个溢写文件经过多轮归并排序最终合并为一个文件;
3.Reduce输入阶段:将同一个分区,不同map task的数据文件进行归并排序
3.1.2.4 SQL语句是如何转换成MR的?
3.1.2.5 Hive的orderby、sortby、distributeby、clusterby的区别
- order by会对输入做全局排序,因此只有一个Reducer(多个Reducer无法保证全局有序),然而只有一个Reducer,会导致当输入规模较大时,消耗较长的计算时间。
- sort by不是全局排序,其在数据进入reducer前完成排序,是一个局部排序的过程,可以保证每个reduce的输出数据都是有序的(但并非全局有效)。这样就可以提高后面进行的全局排序的效率了。
- distribute by是控制在map端如何拆分数据给reduce端的。hive会根据distribute by后面列,对应reduce的个数进行分发,默认是采用hash算法。sort by为每个reduce产生一个排序文件。在有些情况下,你需要控制某个特定行应该到哪个reducer,这通常是为了进行后续的聚集操作。distribute by刚好可以做这件事。因此,distribute by经常和sort by配合使用。
- cluster by除了有distribute by的功能外还会对该字段进行排序兼具sort by的功能,相当于distribute by+sort by。但是排序只能是倒序排序,不能指定排序规则为ASC或者DESC
3.1.2.6 Hive的存储格式(orc等)有啥区别
- TextFile:Hive中默认的存储文件格式,行存储。每一行都是一条记录,每行都以换行符(\n)结尾。数据不做压缩,磁盘开销大,数据解析开销大。文件拷贝至hdfs不进行处理。 优点:最简单的数据格式,便于和其他工具(Pig,grep,sed,awk)共享数据,便于查看和编辑;加载较快 缺点:耗费存储空间,I/O性能较低;Hive不进行数据切分合并,不能进行并行操作,查询效率低。 应用场景:适合于小型查询,查看具体数据内容的和测试操作。
- sequencefile:含有键值对的二进制文件,行存储,Hadoop API提供的一种二进制文件支持,其具有使用方便、可分割、可压缩的特点 优点:可压缩、可分割,优化磁盘利用率和I/O;可并行操作数据,查询效率高 缺点:存储空间消耗最大;对于Hadoop生态系统之外的工具不适用,需要通过text文件转换加载 解释:text转换加载,是指linux的shell端的 -text 命令来查看二进制数据 应用场景:适用于数据量较小,大部分列的查询。
- RC:是一种行列存储相结合的存储方式。首先,将数据按行分块,保证同一个record在一个快上,避免读一个记录需要读取多个block。其次,快数据列式存储,有利于数据压缩和快速的列存储。
- orc文件代表了优化排柱状的文件格式。orc文件格式提供了一种将数据存储在Hive表中的高效方法。这个文件系统实际上是为了克服其他Hive文件格式的限制而设计的。HIve从大型表读取、写入和处理数据时,使用orc文件可以提高性能 数据按行分块 每块按列存储。 特点:压缩快,快速列存取、是rcfile的改良版本。 缺点:加载时性能消耗较大;需要通过text文件转化加载; 应用场景:适用于Hive中大型的存储、查询
3.1.2.7 Spark的宽窄依赖是怎么划分的
- 窄依赖:父 RDD 的一个分区只会被子 RDD 的一个分区依赖;
- 宽依赖:父 RDD 的一个分区会被子 RDD 的多个分区依赖(涉及到shuffle)。
窄依赖的好处:
- 窄依赖的多个分区可以并行计算;
- 窄依赖的一个分区的数据如果丢失只需要重新计算对应的分区的数据就可以了。
宽依赖的好处:
- 划分 Stage(阶段)的依据:对于宽依赖,必须等到上一阶段计算完成才能计算下一阶段。
3.1.2.8 Spark的shuffle过程
- Spark的transform和action算子的区别
- Spark的RDD特性
- SparkSQL的执行过程是什么
版权归原作者 孟意昶 所有, 如有侵权,请联系我们删除。