一. 笔试部分

1. 怎么查看表结构,表创建语句?怎么查看表有哪些分区?怎么查看分区对应hdfs路径?怎么计算某个分区的数据量大小?

  1. 查看表结构
  2. desc formatted table_name;
  3. 查看创建表语句
  4. show create table table_name;
  5. 查看分区信息
  6. show partitions table_name;
  7. 查看分区对应hdfs路径
  8. desc formatted table_name partition (dt='20211218')
  9. 某个分区的数据量大小
  10. hadoop fs -du /user/hive/warehouse/xxx.db/table_xx/xx_dt

2. 有一hive sql,怎么计算这个sql会产生多少个map数?(加面试追问)

切片大小默认是128m一个切片(具体看版本),开启一个maptask。

面试追问:Hadoop 默认块大小设置的依据是什么?为什么默认值设置为128M?这个128是怎么计算出来的?总不能是拍脑袋写的吧?

  1. Math.max(minSize, Math.min(maxSize, blockSize))
  2. minSize=1blockSize=128MmaxSize=Long.MAX_VALUE
  3. 所以,默认情况,splitSize=blockSize=128M,其中:
  4. minsize 通过mapreduce.input.fileinputformat.split.minsize参数控制
  5. maxsize 通过mapreduce.input.fileinputformat.split.maxsize参数控制

面试继续追问:为什么之前版本是64M,现在默认是128M?

(1) HDFS中平均寻址时间大概为10ms;

(2) 经过测试发现,寻址时间为传输时间的1%时,为最佳状态;
所以最佳传输时间为10ms/0.01=1000ms=1s

(3) 目前磁盘的传输速率普遍为100MB/s ,

网卡普遍为千兆网卡传输速率普遍也是100MB/s;

计算出最佳block大小:100MB/s x 1s = 100MB

所以设定block大小为128MB。

(4) 实际在工业生产中,需要经过集群之间的具体情况进行设置。

比如: 跨物理机/机架之间文件传输速率为200MB/s时,一般设定block大小为256MB , 文件传输速率为400MB/s时,一般设定block大小为512MB . 不过最大一般不会超过512MB , 因为目前固态硬盘的读写速率应该不会超过512MB。

面试继续追问:hive on spark怎么考虑map数?

(1)Application:初始化一个SparkContext即生成一个Application;

(2)Job:一个Action算子就会生成一个Job;

(3)Stage:Stage等于宽依赖的个数加1;

(4)Task:一个Stage阶段中,最后一个RDD的分区个数就是Task的个数。

3. 怎么查看hive有什么自带函数?怎么查看函数的详细信息?

  1. show functions;
  2. desc function upper;
  3. upper(str) - Returns str with all characters changed to uppercase

4. 请指出下面sql语句的区别?

  1. select a.* from a left Join b on a.key = b.key and a.ds=xxx and b.ds=xxx
  2. sclect a.* from a lef Join b on a.key = b.key and b.ds=xxx
  3. select a.* from a lef Join b on a.key = b.key and b.ds=xxx where a.ds=xxx
  4. Select a.* from a left Join b on a.key = b.key where a.ds=xxx and b.ds=xxx

数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表。

on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。

where条件是在临时表生成好后,再对临时表进行过滤的条件。条件不为真的就全部过滤掉。

5. 用sql算出res列,即同一个uid下,上一次is_succ=1 时的 id是多少?

id time uid is_suc res
1 2021-01-01 1 1 Null
2 2021-01-02 1 0 1
3 2021-01-03 1 0 1
4 2021-01-04 1 1 1
5 2021-01-05 1 0 4
6 2021-01-06 2 0 Null
7 2021-01-07 2 1 Null
8 2021-01-08 2 0 7
  1. select id,`time`,uid,is_suc,res
  2. from (
  3. select a.id,a.`time`,a.uid,a.is_suc,b.id res,
  4. row_number() over(partition by a.id order by b.id desc) rk
  5. from test a
  6. left join
  7. (select uid,`time`,id from test where is_suc='1') b
  8. on a.uid = b.uid and a.`time`>b.`time` ) c
  9. where rk =1 order by 'time'
  10. ) d

6. 连续3周活跃用户,7天连续3天活跃用户数,写个思路

3周活跃用户

按照设备id对周活进行分组,统计次数大于3次。

如何分析最近七天内连续三天活跃用户数?

(1)查询出最近7天的活跃用户,并对用户活跃日期进行排名

(2)计算用户活跃日期及排名之间的差值

(3)对同用户及差值分组,统计差值个数

(4)将差值相同个数大于等于3的数据取出,然后去重,即为连续3天及以上活跃的用户

7. 一个list [1,5,5,7,9] 去重,并计算时间空间复杂度

时间复杂度是指执行算法所需要的计算工作量

空间复杂度是指执行这个算法所需要的内存空间

  1. let list = [1,5,5,7,9];
  2. function check(list) {
  3. let obj = {};
  4. for (let i = 0; i < list.length; i++) {
  5. let item = list[i];
  6. if (obj[item] != undefined) {
  7. list[i] = list[list.length - 1];
  8. list.length--;
  9. i--;
  10. }
  11. obj[item] = item;
  12. }
  13. return list;
  14. }

只通过一次for循环来判断对象中是否已经存在该值,所以就是 n 的复杂度,也就是 O(n)

8. 单链表的反转

就地反转法

  1. public ListNode reverseList1(ListNode head) {
  2. if (head == null)
  3. return head;
  4. ListNode new_node = new ListNode(-1);
  5. new_node.next = head;
  6. ListNode prev = new_node.next;
  7. ListNode pcur = prev.next;
  8. while (pcur != null) {
  9. prev.next = pcur.next;
  10. pcur.next = new_node.next;
  11. new_node.next = pcur;
  12. pcur = prev.next;
  13. }
  14. return new_node.next;
  15. }

新建链表,头节点插入法

  1. public ListNode reverseList2(ListNode head) {
  2. ListNode new_node = new ListNode(-1);
  3. ListNode pcur = head;
  4. while (pcur != null) {
  5. ListNode pNex = pcur.next;
  6. pcur.next = new_node.next;
  7. new_node.next = pcur;
  8. pcur = pNex;
  9. }
  10. return new_node.next;
  11. }

二. 技术面试部分

1. 我看你简历上有写到熟悉Hive原理和优化,你说一下怎么优化?

可以从以下方面说起。集体细节看以往文章。说出几个方面就行。

(1)小表、大表Join

(2)大表Join大表

(3)MapJoin

(4)开启Map端聚合

(5)笛卡尔积

(6)行列过滤

(7)合理设置Map及Reduce数

(8)并行执行

(9)JVM重用

(10)列式存储与压缩

2. 在Hive执行语句的时候如果很慢,什么原因造成?提到数据倾斜的问题,他又问怎么解决数据倾斜?

可从资源,引擎,数据倾斜,数据量等等方面去说。

追问数据倾斜,怎么解决

先回答

(1)数据预处理。

(2)解决热点数据:分而治之(第一次打散计算,第二次再最终聚合计算)

(3)导致最终只有一个Reduce任务的,需要想到用替代的关键字或者算子去提升Reduce任务数。

(4)调参。

然后细节

(1)group by配置调整

(2)join基础优化

(3)sort by代替order by

(4)单独处理倾斜key

具体细节参考以往文章。

3. 消费kafka数据会不会重复消费,什么情况下会重复消费?

从消息发送和消息消费两个方面去说。

ACK

0:producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据。

1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据。

-1:producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复。

消费

写入sink没有提交offset,下次消费还从处理过的offset消费。这个时候要么做到幂等不影响下游。要么就是事务+两阶段提交。

4. 你知道Spark的宽窄依赖吗? 任务是怎么切分的?

窄依赖是指父RDD的每个分区只被子RDD的一个分区所使用,子RDD分区通常对应常数个父RDD分区。

宽依赖是指父RDD的每个分区都可能被多个子RDD分区所使用,子RDD分区通常对应所有的父RDD分区。

(1)Application:初始化一个SparkContext即生成一个Application;

(2)Job:一个Action算子就会生成一个Job;

(3)Stage:Stage等于宽依赖的个数加1;

(4)Task:一个Stage阶段中,最后一个RDD的分区个数就是Task的个数。

5. 你们数仓用什么建模方法,说出常见的建模方法?

(1)范式建模法

从关系型数据库的角度出发,结合了业务系统的数据模型,能够比较方便的实现数据仓库的建模。然而,在某些时候反而限制了整个数据仓库模型的灵活性,性能等问题。

(2)维度建模法

维度建模非常直观,紧紧围绕着业务模型,可以直观的反映出业务模型中的业务问题。

(3)实体建模法

客观世界应该是可以细分的,客观世界应该可以分成由一个个实体,以及实体与实体之间的关系组成。

追问:维度建模

星型模型(一级维度表)

雪花(多级维度)

星座模型(星型模型+多个事实表)

追问:维度退化怎么理解

当一个维度没有数据仓库需要的任何数据时就可以退化此维度。需要把维度退化的相关数据迁移到事实表中,然后删除退化的维度。

维度属性也可以存储到事实表中,这种存储到事实表中的维度列被称为“维度退化”。与其他存储在维表中的维度一样 , 维度退化也可以用来进行事实表的过滤查询、实现聚合操作等。

6. 维度建模有什么好处? 为什么选择这个?

维度建模非常直观,紧紧围绕着业务模型,可以直观的反映出业务模型中的业务问题。不需要经过特别的抽象处理,即可以完成维度建模。

星型模式之被广泛使用,针对各个维度作了大量的预处理,如的统计、分类、排序等。通过这些预处理,提升数据仓库的处理能力。

7. 怎么判断一个需求能不能实现,你们的判断标准是什么?

一个需求实现,还与产品或者领导划定的交付时间有关系,还与现有的公司的技术,架构,和人才涉及技术方面有关系。

需求来了要讨论,怎么写,用什么技术,实现逻辑是什么,技术难点在哪。 拿到需求一般要开会,多人一起分析拆解需求,转变成一条条可实现的技术,如果出现了无法评估可行性的需求,就要去做技术调研,也就是查资料,问朋友,或者花钱买别人的代码/接口。

8. 挑一个比较有亮点的项目详细说

从 0 到 1 的项目,在这个项目中,数据仓库的建模,维度选择,数仓的分层等等,到最后指标开发都一路参与其中,从中了解到了数据的流转和整个数据仓库的生命周期。在这个项目中是一种质的飞跃。也主动承担了一些责任感的角色,去推动项目正常运行,保证了项目正常上线 . . . . . .

这个问题可以结合自己实际工作经验去说,自己也要准备好说辞去应对面试提问在项目中遇到的难点,最后怎么解决的;自身的优缺点等等这类主观性问题。