大数据面试总结 [面经分享] 2018/12/13

上午面试: 神州优车

面试问题:

1. 数据仓库建模方面

  • 数据仓库主要是通过维度建模技术, 主要有两个概念: 事实表和维度表
  • 接下来说一些维度建模的三种模型: 星星模型, 雪花模型, 星座模型
  • 可以举个栗子说明一下应用场景:
    • 例如有以下表: 产品表,产品分类表,商家表,用户表,订单表. 然后确定我们的主题, 例如是销售, 接着我们要确定哪些维度对于我们分析销售这个主题有帮助, 比如产品/商家/日期/用户, 然后我们看下这几个表的一个外部关联关系, 产品关联了产品分类表, 另外把订单表里的订单时间给拿出来切分一下, 例如切分成周/月度/年度然后作为一个时间维度表.
    • 接下来就有了这几个维度表: 产品表这个里面还有个产品分类的名字合并到一起, 用户表, 日期表, 商家表. 然后事实表中当中会有外键一一关联这些维度表.
    • 那么这样就形成了一个星型模型. 其实稍微做个变形就会形成雪花模型, 例如在产品维度表之外再关联一个产品分类维度表, 这样维度表外再关联维度表, 就形成了雪花模型. 但是实际业务中基本不会使用雪花模型, 基本停留在理论层面, 因为我雪花模型增大了我们的查询难度.
  • 到这里停一下, 看面试官是否露出感兴趣的意思.
  • 也可以接着说: 在上面的基础上在做变形就会得到星座模型, 比如现在又要分析劣质产品主题, 那现在就不需要重新做数据仓库建模, 只需要在上面的星型模型中加入劣质产品事实表, 然后共享这些维度. 这样子多个事实表, 共享维度表就形成了星座模型.
  • 如果面试官听的很有兴趣说明他对这方面有意向, 那就可以接着介绍: 事实表一般分为两种: 细节事实表和聚集事实表, 细节事实表就是每条记录只对应一个事实, 聚集事实表是一条记录聚集了多个事实. 所以细节事实表查询起来会比较灵活但是对应的响应速度慢, 聚集事实表速度快但不灵活. 所以通常情况下, 我们可以使用星座模型来综合两个事实表. 比如上面这个例子, 可以细节事实表是对应到每一个产品的销量和销售额, 而聚集表就不对应到每个产品, 只聚焦到哪个用户什么时间哪个商家即可.
  • 接着如果面试官听的比较起劲儿, 可能会问你, 那你来说说缓慢变化维表. 那你就可以继续说了: 我们在事实表和维度表主外键关联的时候, 通常情况下不是使用的比如ProductID, 而是另外一个字段比如ProductKey, 这就是考虑到维度变化的问题, 这样在维度表中通常就有两个字段: 一个是ProduceKey, 一个是isCurrent, 如果有维度变化了那么只需要添加一条记录即可, ProduceID不变, 但是ProduceKey变化了, 而且之前的isCurrent变成了Not Current, 而新修改的记录则为Current. 如此我们在查询的时候, 如果想join所有, 那么只需要按照ProductID来join即可. 如果想要join当前的, 那就筛选出isCurrent字段为Current的即可.
  • 面试官可能会细细的考你, 不过你也可以直接说, 不给他提问的机会: 当然如果我们需要查找某个历史时间的数据, 那么是用isCurrent字段就不能满足了, 通常情况下我们会增加两个字段: start_date, end_date. 新增数据与之前的维表合并的时候, 历史数据的end_date修改为昨天的时间, 新修改的数据的end_date为’9999-12-31’, 就是指代这条数据的过期时间是永久.
  • 那说到这里基本上已经说得差不多了, 再来个完美的结尾即可. 数据仓库建模体系总体上分为三种: 规范化数据仓库, 维度建模数据仓库, 独立集市数据仓库. 通常在企业中使用最多的就是维度建模数据仓库了. 首先独立集市数据仓库是让每个部门的开发人员自己去做ETL, 自己去搭建自己的数据集市, 如此其实对于企业来说不是很好, 因为造成了信息分散, 分析结果不能重复利用等问题. 前两种规范化数据仓库在前期需要对全局进行规范化建模, 需要投入很大的工作量, 时间成本较大, 但是冗余字段较少, 后期方便维护. 维度建模能够快速投入使用, 但是会有较多的冗余字段.
  • 当然了, 如果你专门面试的是数据仓库建模方面的工作, 那么面试官肯定更详细的询问你了, 例如你的事实表怎么划分啊, 分别有哪些优缺点和适用场景啊, 或者问你事实表的存储策略, 存储空间不够了采取什么办法, 甚至给你个业务场景, 让你手写下这个场景中涉及到的事实表和维度表, 也有可能让你手写实现维度表代理键如何实现自增? 手写下如何给事实表装载代理键? 用hive手写个增量/全量情况下拉链表的构建语句…等等.

2. 数据仓库为什么分层

  • 首先也是必须要来说一下为什么需要分层? 主要分三个点来说: 第一呢, 用空间换时间, 通过大量的预处理来提升效率. 第二呢, 不分层的话, 如果业务规则发生变化将会影响整个数据清洗过程, 需要重头再来, 工作量巨大. 第三呢, 将一个复杂的工作拆分成一个个的小任务, 每一层的逻辑都相对简单, 更容易保证每一步骤的正确性. 发生错误的时候, 只需要局部调整某个步骤就可以了.
  • 接着也是必须要说一下有哪几层, 数据仓库标准上分为四层, 分别是ODS层(临时存储层), PDW层(数据仓库层), MID层(数据集市层), APP(应用层). 这四层的介绍就不多说了, 应该都是很熟悉的, 基本说完这些面试官不会在这里为难你.

下午面试: 松鼠拼拼

面试问题:

1. Hive优化和spark优化问题

这块优化手段有很多, 挑着自己比较熟的说几个就行.

2. 对空key加随机字符串防止数据倾斜, 写一下这个SQL语句

SELECT a.*
FROM nullidtable a
LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN concat('hive', rand()) ELSE a.id END = b.id;

3. 写一下在join出现数据倾斜的时候怎么处理的代码

这块面试官让本人在A4纸上直接手写, 本人写的那叫一个磕磕绊绊啊, 实在是处理逻辑都记得, 在A4纸上写是真的写不顺畅.

回到家, 感觉翻出以前的笔记, 使劲儿多敲了几遍, 才算舒了口气~

// reduceBykey加随机数避免数据倾斜
val preRDD = rdd.map(x=>{
	Random random = new Random()
	int pre = random.nextInt(100);
	(pre+"_"+x.1, x._2)  //1_hello  2_hello
})
val firReduce = preRDD.reduceBykey(+)
val secRDD = firReduce.map(x=>{
	val key = x.1.split("_")[1]
	(key, x._2)
})
val secReduce = secRDD.reduceBykey(+)


// 小数据量与大数据量join时避免数据倾斜
val rdd1 = rdd1.collect()
val broadcastRdd1 = sc.broadcast(rdd1)
rdd2.map(x=>{
	val rdd1Data = broadcastRdd1.value()
	val map = HashMap()
	for (data <- rdd1Value){
		map.append(data.1,data.2)
	}
	val rdd1Value = map.get(x._1)
	(x.1, (x.2, rdd1Value))
})
 

// 采样倾斜key并分拆join操作
val sampleRDD = rdd1.sample(false,0.1)
topIds = sampleRDD.map((.1, 1)).reduceBykey(+).sortBy(.2,false).map(.1).take(100).collect()
val filterRDD1 = rdd1.filter(line=>{
	topIds.contains(line)
})
val commonRDD1 = rdd1.filter(line=>{
	!topIds.contains(line)
})
val filterRDD2 = rdd2.filter(line=>{
	topIds.contains(line)
}).flatMap(x=>{
	val list = BufferList();
	for (i <- 1 to 100){
		list += (i+"_"+x.1, x._2)
	}
	list
})
val joinedRDD1 = filterRDD1.map(x=>{
	Random random = new Random()
	int pre = random.nextInt()
	(pre+"_"+x.1, x._2)
})
val joinedRDD1 = joinedRDD1.join(filterRDD2)
val joinedRDD2 = commonRDD1.join(rdd2)
val joinedRDD = joinedRDD1.union(joinedRDD2)

4. 写一下分类求取TopN的SQL

这个很简单了, 就不写了.

5. Linux监控命令用过哪些 (df/dh/free等简单的不用说了) 重点可以说说JVM监控

jstat -class 2041 查看加载了多少类以及对应的信息

jstat -compiler 2041 查看编译的数量, 成功和失败的数量和类型

jstat -gc 2041 查看垃圾回收的统计信息

通过jstat可以对jvm堆的内存进行统计分析,而jmap可以获取到更加详细的内容

如:内存使用情况的汇总、对内存溢出的定位与分析。

jmap -heap 2041 heap内存的使用情况

jmap -histo 2041 | more 查看内存中对象数量及大小

jmap -histo:live 2041 | more 查看内存中对象活跃数量及大小

jmap -dump:format=b,file=/tmp/dump.dat 6219 将内存使用情况dump到文件中

jhat -port 9999 dump.dat 通过jhat对dump文件进行分析

最后还有个Htop, 属于top的升级版, 需要先使用yum安装, 之后就使用htop命令就可以进入界面

6. MapReduce的处理流程, map端和reduce端分别用的什么排序算法

在Map阶段,k-v溢写时,采用的是快排;而溢出文件的合并使用的则是归并;在Reduce阶段,通过shuffle从Map获取的文件进行合并的时候采用的也是归并;最后阶段则使用了堆排作最后的合并过程。

所以快排、归并以及堆排是必须要掌握的排序算法,这都在MapReduce内部使用的排序算法,学习Hadoop的必须过程。

7. 用shell写个Wordcount程序

#!/bin/bash

if [ $# -ne 1 ]
then
	echo "Usage: $0 filename"
	exit -1
fi

cat $1 | tr 'A-Z' 'a-z' | egrep -o "\b[[:alpha:]]+\b" | \
awk '{ count[$0]++ } 
END {
	for(ind in count){
		printf("%-14s%d\n",ind,count[ind]);
	}
}' | sort -k 2 -n -r

egrep命令: 按照正则表达式的规则匹配
-o: 仅输出匹配到的单词, 不输出那一行
awk: 行处理器, 这玩意儿厉害了
$0: awk里面的$0指的是一行, 和shell的$0不一样哦
END: 是指前面的每一行都执行过后再执行后面的
sort: 排序
-k: 指定排序的列
-n: 按照数字排序, 非字典序
-r: 降序排序

你可能感兴趣的:(大数据)