快手数仓面试题附答案

题目

  • 1 讲一下你门公司的大数据项目架构?
  • 2 你在工作中都负责哪一部分
  • 3 spark提交一个程序的整体执行流程
  • 4 spark常用算子列几个,6到8个吧
  • 5 transformation跟action算子的区别
  • 6 map和flatmap算子的区别
  • 7 自定义udf,udtf,udaf讲一下这几个函数的区别,编写的时候要继承什么类,实现什么方法
  • 8 hive创建一个临时表有哪些方法
  • 9 讲一下三范式,三范式解决了什么问题,有什么优缺点
  • 10 讲一下纬度建模的过程
  • 11 纬度表有哪几种
  • 12 事实表有几种
  • 13 什么是纬度一致性,总线架构,事实一致性
  • 15 什么是拉链表,如何实现?
  • 16 什么是微型纬度、支架表,什么时候会用到
  • 17 讲几个你工作中常用的spark 或者hive 的参数,以及这些参数做什么用的
  • 18 工作中遇到数据倾斜处理过吗?是怎么处理的,针对你刚刚提的方案讲一下具体怎么实现。用代码实现,以及用sql实现。
  • 19 讲一下kafka对接flume 有几种方式。
  • 20 讲一下spark是如何将一个sql翻译成代码执行的,里面的原理介绍一下?
  • 21 spark 程序里面的count distinct 具体是如何执行的
  • 22 不想用spark的默认分区,怎么办?(自定义Partitioner 实现里面要求的方法 )具体是哪几个方法?
  • 23 有这样一个需求,统计一个用户的已经曝光了某一个页面,想追根溯是从哪几个页面过来的,然后求出在这几个来源所占的比例。你要怎么建模处理?
  • 23 说一下你对元数据的理解,哪些数据算是元数据
  • 24 有过数据治理的经验吗?
  • 25 说一下你门公司的数据是怎么分层处理的,每一层都解决了什么问题
  • 26 讲一下星型模型和雪花模型的区别,以及应用场景

答案

1 讲一下你门公司的大数据项目架构?

实时流和离线计算两条线

数仓输入(客户端日志,服务端日志,数据库)

传输过程(flume,kafka)

数仓输出(报表,画像,推荐等)

2 你在工作中都负责哪一部分

3 spark提交一个程序的整体执行流程

包括向yarn申请资源、DAG切割、TaskScheduler、执行task等过程

4 spark常用算子列几个,6到8个吧

常用的RDD转换算子:

  1. filter(func) 筛选出满足函数func的元素,并返回一个新的数据集
  2. map(func) 将每个元素传递到函数func中,并将结果返回为一个新的数据集
  3. flatMap(func) 与map()相似,但每个输入元素都可以映射到0或多个输出结果
  4. groupByKey() 应用于(K,V)键值对的数据集时,返回一个新的(K, Iterable)形式的数据集
  5. reduceByKey(func) 应用于(K,V)键值对的数据集时,返回一个新的(K, V)形式的数据集,其中每个值是将每个key传递到函数func中进行聚合后的结果

行动操作常用算子:

  1. count() 返回数据集中的元素个数
  2. collect() 以数组的形式返回数据集中的所有元素
  3. first() 返回数据集中的第一个元素
  4. take(n) 以数组的形式返回数据集中的前n个元素
  5. reduce(func) 通过函数func(输入两个参数并返回一个值)聚合数据集中的元素
  6. foreach(func) 将数据集中的每个元素传递到函数func中运行

5 transformation跟action算子的区别

所有的transformation都是采用的懒策略,就是如果只是将transformation提交是不会执行计算的,计算只有在action被提交的时候才被触发。

  • Transformation 变换/转换:这种变换并不触发提交作业,完成作业中间过程处理。Transformation 操作是延迟计算的,也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行,需要等到有 Action 操作的时候才会真正触发运算。
  • Action 行动算子:这类算子会触发 SparkContext 提交 Job 作业。
    Action 算子会触发 Spark 提交作业(Job)。

transformation操作:

  1. map(func):对调用map的RDD数据集中的每个element都使用func,然后返回一个新的RDD,这个返回的数据集是分布式的数据集
  2. filter(func): 对调用filter的RDD数据集中的每个元素都使用func,然后返回一个包含使func为true的元素构成的RDD
  3. flatMap(func):和map差不多,但是flatMap生成的是多个结果
  4. mapPartitions(func):和map很像,但是map是每个element,而mapPartitions是每个partition
  5. mapPartitionsWithSplit(func):和mapPartitions很像,但是func作用的是其中一个split上,所以func中应该有index
  6. sample(withReplacement,faction,seed):抽样
  7. union(otherDataset):返回一个新的dataset,包含源dataset和给定dataset的元素的集合
  8. distinct([numTasks]):返回一个新的dataset,这个dataset含有的是源dataset中的distinct的element
  9. groupByKey(numTasks):返回(K,Seq[V]),也就是hadoop中reduce函数接受的key-valuelist
  10. reduceByKey(func,[numTasks]):就是用一个给定的reducefunc再作用在groupByKey产生的(K,Seq[V]),比如求和,求平均数
  11. sortByKey([ascending],[numTasks]):按照key来进行排序,是升序还是降序,ascending是boolean类型
  12. join(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,(V,W))的dataset,numTasks为并发的任务数
  13. cogroup(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,Seq[V],Seq[W])的dataset,numTasks为并发的任务数
  14. cartesian(otherDataset):笛卡尔积就是m*n,大家懂的

action操作:

  1. reduce(func):说白了就是聚集,但是传入的函数是两个参数输入返回一个值,这个函数必须是满足交换律和结合律的
  2. collect():一般在filter或者足够小的结果的时候,再用collect封装返回一个数组
  3. count():返回的是dataset中的element的个数
  4. first():返回的是dataset中的第一个元素
  5. take(n):返回前n个elements,这个士driverprogram返回的
  6. takeSample(withReplacement,num,seed):抽样返回一个dataset中的num个元素,随机种子seed
  7. saveAsTextFile(path):把dataset写到一个textfile中,或者hdfs,或者hdfs支持的文件系统中,spark把每条记录都转换为一行记录,然后写到file中
  8. saveAsSequenceFile(path):只能用在key-value对上,然后生成SequenceFile写到本地或者hadoop文件系统
  9. countByKey():返回的是key对应的个数的一个map,作用于一个RDD
  10. foreach(func):对dataset中的每个元素都使用func

参考:Spark常用算子详解_spark算子-CSDN博客

6 map和flatMap算子的区别

  • map:执行完map后会得到一个新的分布式数据集,数据集中每个元素是之前的RDD映射得来的,与之前RDD每个元素存在一一对应的关系。
  • flatmap:而flatmap有一点不同,每个输入的元素可以被映射为0个或者多个输出的元素,原RDD与新RDD的元素是一对多的关系。当然光看定义比较抽象,下面用一个图说明,

参考:Spark之Map VS FlatMap - 知乎

7 自定义udf,udtf,udaf讲一下这几个函数的区别,编写的时候要继承什么类,实现什么方法

区别:

  • UDF:输入一行,输出一行
    UDF:用户定义(普通)函数,只对单行数值产生作用;
  • UDTF:输入一行,输出多行,类似explode函数
    UDTF:User-Defined Table-Generating Functions,用户定义表生成函数,用来解决输入一行输出多行;
  • UDAF:输入多行,输出一行,类似聚合函数
    UDAF:User- Defined Aggregation Funcation;用户定义聚合函数,可对多行数据产生作用;等同与SQL中常用的SUM(),AVG(),也是聚合函数;

Hive实现:

|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 类型 | 类 | 方法 |
| UDF | 类: GenericUDF 包路径: org.apache.hadoop.hive.ql.udf.generic | initialize:类型检查,返回结果类型 入参:ObjectInspector[] 出参:ObjectInspector evaluate:功能逻辑实现 入参:DeferredObject[] 出参:Object getDisplayString:函数名称 入参:String[] 出参:String close:关闭函数,释放资源等 入参:无 出参:void |
| UDTF | 类: GenericUDTF 包路径: org.apache.hadoop.hive.ql.udf.generic | initialize:类型检查,返回结果类型 入参:StructObjectInspector 出参:StructObjectInspector process:功能逻辑实现 **调用forward输出一行数据,可多次调用 入参:Object[] 出参:void close:关闭函数,释放资源等 入参:无 出参:void |
| UDAF | 类: AbstractGenericUDAFResolver 包路径: org.apache.hadoop.hive.ql.udf.generic 类: GenericUDAFEvaluator 包路径: org.apache.hadoop.hive.ql.udf.generic 类: AbstractAggregationBuffer 包路径: org.apache.hadoop.hive.ql.udf.generic | -----AbstractGenericUDAFResolver----- getEvaluator:获取计算器 入参:TypeInfo[] 出参:GenericUDAFEvaluator ---------GenericUDAFEvaluator---------- init: 入参:Mode,ObjectInspector[] 出参:ObjectInspector getNewAggregationBuffer: 入参:无 出参:AggregationBuffer reset: 入参:AggregationBuffer 出参:void iterate: 入参:AggregationBuffer,Object[] 出参:void merge: 入参:AggregationBuffer,Object 出参:void terminate: 入参:AggregationBuffer 出参:Object terminatePartial: 入参:AggregationBuffer 出参:Object --------AbstractAggregationBuffer------- estimate:评估内存占用大小 入参:无 出参:int |

UDAF说明

  • 一个Buffer作为中间处理数据的缓冲:获取getNewAggregationBuffer、重置reset
  • 四个阶段(Mode):
    1. PARTIAL1(Map阶段):
      from original data to partial aggregation data:
      iterate() and terminatePartial() will be called.
    2. PARTIAL2(Map的Combiner阶段):
      from partial aggregation data to partial aggregation data:
      merge() and terminatePartial() will be called.
    3. FINAL(Reduce 阶段):
      from partial aggregation to full aggregation:
      merge() and terminate() will be called.
    4. COMPLETE(Map Only阶段):
      from original data directly to full aggregation:
      iterate() and terminate() will be called.
  • 五个方法:
    1. 初始化init
    2. 遍历iterate:PARTIAL1和COMPLETE阶段
    3. 合并merge:PARTIAL2和FINAL阶段
    4. 终止terminatePartial:PARTIAL1和PARTIAL2阶段
    5. terminate:COMPLETE和FINAL阶段

Spark实现:

参考:Spark - 自定义函数(UDF、UDAF、UDTF) - 知乎

8 hive创建一个临时表有哪些方法

WITH创建临时表

如果这个临时表并不需要保存,并且下文只需要用有限的几次,我们可以采用下面的方法。

with as 也叫做子查询部分,首先定义一个sql片段,该sql片段会被整个sql语句所用到,为了让sql语句的可读性更高些,作为提供数据的部分,也常常用在union等集合操作中。

with as就类似于一个视图或临时表,可以用来存储一部分的sql语句作为别名,不同的是with as 属于一次性的,而且必须要和其他sql一起使用才可以!

其最大的好处就是适当的提高代码可读性,而且如果with子句在后面要多次使用到,这可以大大的简化SQL;更重要的是:一次分析,多次使用,这也是为什么会提供性能的地方,达到了"少读"的目标。

sql 复制代码
WITH t1 AS (
        SELECT *
        FROM a
    ), 
    t2 AS (
        SELECT *
        FROM b
    )
SELECT *
FROM t1
JOIN t2
;

注意:

  • 这里必须要整体作为一条sql查询,即with as语句后不能加分号,不然会报错。
  • with子句必须在引用的select语句之前定义,同级with关键字只能使用一次,多个只能用逗号分割
  • 如果定义了with子句,但其后没有跟select查询,则会报错!
  • 前面的with子句定义的查询在后面的with子句中可以使用。但是一个with子句内部不能嵌套with子句!

Temporary创建临时表

sql 复制代码
create temporary table 临时表表名 as 
select * from 表名
  • 创建的临时表仅仅在当前会话可见,数据会被暂存到hdfs上,退出当前会话表和数据将会被删除。数据将存储在用户的scratch目录中,并在会话结束时删除。

  • 从Hive1.1开始临时表可以存储在内存或SSD,使用hive.exec.temporary.table.storage参数进行配置,该参数有三种取值:memory、ssd、default。
    如果内存足够大,将中间数据一直存储在内存,可以大大提升计算性能。

  • 如果临时表的命名的表名和hive的表名一样,当前会话则会查询临时表的数据,用户在这个会话内将不能使用原表,除非删除或者重命名临时表

  • 临时表不支持分区字段,不支持创建索引。

参考:大数据开发之Hive篇7-Hive临时表 - 知乎

9 讲一下三范式,三范式解决了什么问题,有什么优缺点

三范式:

  • 第一范式:列的原子性,字段值不可再分,比如某个字段的取值是姓名+手机号,那就要把姓名和手机号分成两个字段
  • 第二范式:第一范式的基础上,非主键列不能依赖主键的一部分,例如字段a和字段b组成的主键,某个字段只依赖a,就需要把这个字段剥离到a对应的表
  • 第三范式:第二范式的基础上,非主键列不能传递依赖主键,例如字段c依赖字段b,字段b依赖主键字段a,那么就可以把这个字段c剥离到字段b为主键的表

三范式是要解决字段冗余,节省存储空间,数据维护更方便,不需要多处更新同样的字段;

缺点是不方便查询,要进行多表join效率低,不适合分析类的查询。

范式化设计的优点:可以减少数据冗余,数据表体积小更新快,范式化的更新操作比 反范式化更快,范式化的表通常比反范式化更小。

缺点:对于查询需要对多个表,会关联多个表,在应用中,进行表关联的成本是很高

更难进行索引优化

反范式化设计的优点:可以减少表的关联,可以对查询更好的进行索引优化,

缺点:表结构存在数据冗余和数据维护异常,对数据的修改需要更多资源。

因此在设计数据库结构的时候要将反范式化和范式化结合起来
参考:你了解数据库三大范式吗?用来解决什么问题?_数据库三范式解决了什么问题_我是等闲之辈的博客-CSDN博客mysql--数据库优化的目的、数据库设计的步骤以及什么是三范式、三范式的优缺点-CSDN博客

10 讲一下纬度建模的过程

(选择业务过程 确定粒度 确定纬度 确定事实表)

11 纬度表有哪几种

12 事实表有几种

13 什么是纬度一致性,总线架构,事实一致性

15 什么是拉链表,如何实现?

16 什么是微型纬度、支架表,什么时候会用到

17 讲几个你工作中常用的spark 或者hive 的参数,以及这些参数做什么用的

18 工作中遇到数据倾斜处理过吗?是怎么处理的,针对你刚刚提的方案讲一下具体怎么实现。用代码实现,以及用sql实现。

19 讲一下kafka对接flume 有几种方式

三种:source、channel、sink

source和sink对接方式:Flume对接Kafka详细过程_flume kafka_杨哥学编程的博客-CSDN博客

channel对接方式:flume--KafkaChannel的使用_kafka channel为什么没有sink-CSDN博客

20 讲一下spark是如何将一个sql翻译成代码执行的,里面的原理介绍一下?

SparkSQL主要是通过Catalyst优化器,将SQL翻译成最终的RDD算子的

|----|-----------------------------------|---------------|
| 阶段 | 产物 | 执行主体 |
| 解析 | Unresolved Logical Plan(未解析的逻辑计划) | sqlParser |
| 分析 | Resolved Logical Plan(解析的逻辑计划) | Analyzer |
| 优化 | Optimized Logical Plan(优化后的逻辑计划) | Optimizer |
| 转换 | Physical Plan(物理计划) | Query Planner |

无论是使用 SQL语句还是直接使用 DataFrame 或者 DataSet 算子,都会经过Catalyst一系列的分析和优化,最终转换成高效的RDD的操作,主要流程如下:

  1. sqlParser 解析 SQL,生成 Unresolved Logical Plan(未解析的逻辑计划)

  2. 由 Analyzer 结合 Catalog 信息生成 Resolved Logical Plan(解析的逻辑计划)

  3. Optimizer根据预先定义好的规则(RBO),对 Resolved Logical Plan 进行优化并生成 Optimized Logical Plan(优化后的逻辑计划)

  4. Query Planner 将 Optimized Logical Plan 转换成多个 Physical Plan(物理计划)。然后由CBO 根据 Cost Model 算出每个 Physical Plan 的代价并选取代价最小的 Physical Plan 作为最终的 Physical Plan(最终执行的物理计划)

  5. Spark运行物理计划,先是对物理计划再进行进一步的优化,最终映射到RDD的操作上,和Spark Core一样,以DAG图的方式执行SQL语句。 在最新的Spark3.0版本中,还增加了Adaptive Query Execution功能,会根据运行时信息动态调整执行计划从而得到更高的执行效率

整体的流程图如下所示:

参考:SparkSQL运行流程浅析_简述spark sql的工作流程-CSDN博客

21 spark 程序里面的count distinct 具体是如何执行的

  • 一般对count distinct优化就是先group by然后再count,变成两个mapreduce过程,先去重再count。

  • spark类似,会发生两次shuffle,产生3个stage,经过4个步骤:①先map端去重,②然后再shuffle到reduce端去重,③然后通过map做一次partial_count,④最后shuffle到一个reduce加总。

  • spark中多维count distinct,会发生数据膨胀问题,会把所有需要 count distinct 的N个key组合成List,行数就翻了N倍,这时最好分开来降低单个任务的数据量。

参考:大数据SQL COUNT DISTINCT实现原理 - 知乎

22 不想用spark的默认分区,怎么办?(自定义Partitioner 实现里面要求的方法 )具体是哪几个方法?

Scala 复制代码
abstract class Partitioner extends Serializable {
  def numPartitions: Int
  def getPartition(key: Any): Int
}

参考:Spark自定义分区器-CSDN博客

23 有这样一个需求,统计一个用户的已经曝光了某一个页面,想追根溯是从哪几个页面过来的,然后求出在这几个来源所占的比例。你要怎么建模处理?(这里回答的不好,挺折磨的。面试官的意思是将所有埋点按时间顺序存在一个List 里,然后可能需要自定义udf函数,更主要的是考虑一些异常情况,比如点击流中间是断开的,或者点击流不全,怎么应对)

23 说一下你对元数据的理解,哪些数据算是元数据

24 有过数据治理的经验吗?

25 说一下你门公司的数据是怎么分层处理的,每一层都解决了什么问题

26 讲一下星型模型和雪花模型的区别,以及应用场景

相关推荐
zmd-zk39 分钟前
kafka+zookeeper的搭建
大数据·分布式·zookeeper·中间件·kafka
激流丶41 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
测试界的酸菜鱼1 小时前
Python 大数据展示屏实例
大数据·开发语言·python
时差9531 小时前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
Mephisto.java1 小时前
【大数据学习 | kafka高级部分】kafka中的选举机制
大数据·学习·kafka
Mephisto.java1 小时前
【大数据学习 | kafka高级部分】kafka的优化参数整理
大数据·sql·oracle·kafka·json·database
道可云1 小时前
道可云人工智能&元宇宙每日资讯|2024国际虚拟现实创新大会将在青岛举办
大数据·人工智能·3d·机器人·ar·vr
成都古河云1 小时前
智慧场馆:安全、节能与智能化管理的未来
大数据·运维·人工智能·安全·智慧城市
软工菜鸡1 小时前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
武子康3 小时前
大数据-212 数据挖掘 机器学习理论 - 无监督学习算法 KMeans 基本原理 簇内误差平方和
大数据·人工智能·学习·算法·机器学习·数据挖掘