#面试指导
如果看到“硕士”、“985、211院校”,这些大概率是硬性条件。如果条件不符,可以不投。
面试心态
开始的2-3次,预期不会通过
第3-5次,有面试的感觉,熟悉了企业的常规的面试流程和面试官的风格
第4次后,学会了应变,有信心去面对各种应聘的困难
最后一定能找到工作。只是时间早晚问题。
年龄小是优势,不要过于担心,互联网行业拥抱年轻人
我们的知识面足够广,主流的组件一个都不缺,而那些所谓的企业4-5年经验的工程师,经常有偏科的情况
不要排斥外包,可以通过外包进入一线大厂,接触大厂的需求。比如腾讯,阿里等。可以接触到他们的IT架构,开发模式,组织架构,管理制度。外包薪资普遍不低。
相对于一线互联网大厂,难度较大,各行业的头部公司的IT部门也不错。
先进公司再说,就算薪资不高也不要灰心,在职工作半年,努力积累技术,为第二次跳槽做准备。
大数据程序员=70%开发工程师+10%测试+10%运维+10%业务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28Upus20-1665888837467)(面试指导.assets/1639052814339.png)]
注意事项
专业技能
项目描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLCREpvY-1665888837468)(面试指导.assets/1639054318360.png)]
简化后
该项目是基于用户在美客多网站产生的日志结合历史数据完成业务需求分析。帮助运营决策分析,(该项目为何而来)。该项目数据采集自网站的日志服务器,并落入HDFS,完成了大数据仓库的Hive的ODS层建设(项目使用什么技术来分析数据)。通过SparkSQL整合Hive分析了用户留存率,网站活跃,地区分布等指标(分析了哪些指标)。最终用Superset展示报表数据。
项目职责
项目讲解:
“你们的项目用了多少节点,数据量是多少,团队人数,一个Spark任务分配多少资源。”
“你在工作中遇到了什么技术困难,怎么解决的”
1、在双11、双12, 618等活动中,数据量暴增,造成当日(月)的数据暴增,引起跑批失败,在18080历史日志服务器,发现以前的shuffle并行度200不够了,调整为400,
set spark.sql.shuffle.partitions=400;再次跑批成功。
2、或者发现跑批时,通过4040监控页面发现一个job一直有几个Task一直卡着不动。在程序中对初始RDD数据进行重分区rdd.repartition(200)
select /*+ repartition(200) */ * from table1 a join table2 b on a.id=b.id;
3、发现Spark应用程序耗时比预计要久,大概是时间翻倍,通过4040监控页面发现2个job都耗时很久,但是他们其实是复用了RDD的,所以优化调整RDD进行持久化调用rdd.persist(StorageLevel.MEMOR_AND_DISK_2),再次运行,时间降低一半。
在Spark应用中的几个job复用了一个Dataset,但是跑得比预期慢,通过Spark ui监控页面,发现Dataset重复计算了多次,所以将公共的Dataset进行persist,cache,checkpoint操作,耗时大大降低。
几种常见的架构
####一道spark内存题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3hSblU01-1665888837470)(面试指导.assets/1628061600963.png)]
默认情况下,executor的内存划分是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECo5WFow-1665888837471)(面试指导.assets/1637218262530.png)]
此题的比例是:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-if231PF9-1665888837471)(面试指导.assets/1637218212998.png)]
####启动spark应用的几种方式?
1,spark/bin/spark-submit :企业中用的最多,【生产上线】使用。
2 , spark/bin/pyspark :用在【自测】阶段。-提供给擅长python的开发者
3, spark/bin/spark-sql:用在【自测】阶段。-提供给擅长SQL的开发者
4, spark/bin/spark-shell:用在【自测】阶段。-提供给擅长scala的开发者
在Pycharm中写完Spark程序main方法后,右键run运行,也可以产生一个应用。
5 , spark/sbin/start-thriftserver.sh , 启动一个长期的驻留服务Spark Thriftserver服务。用作分布式JDBC引擎。在企业中只启动一个主服务,分配足够多的资源,部门的各个用户提交SQL,可以连接这个服务来查询结果。
一般用local或者standalone方式,会启动【4040】端口监控页面–用在【自测】阶段。如果是yarn方式,则是8088的proxy代理页面。
####spark应用的运行方式有几种,分别是怎样的?
单机版
集群版
【了解】Spark自带的资源管理器。
【重点】Hadoop的Yarn的资源管理器
spark-submit --master 【yarn】就是Spark On Yarn
deploy-mode分为
client
在【提交Spark应用的客户端机器上】启动Driver进程
spark-submit \
--master yarn \
--deploy-mode client
--driver-cores 1 \
--driver-memory 2G \
--num-executors 20 \
#一个executor放置3-5个core,通常是4
--executor-cores 4 \
#一个core可以支配3-5G的内存。
--executor-memory 12G \
wordcount.py
cluster:在【随机的空闲的WorkerNode节点上】启动Driver进程。企业中用的最多。
spark-submit \
--master yarn \
--deploy-mode cluster
--driver-cores 1 \
--driver-memory 2G \
--num-executors 20 \
#一个executor放置3-5个core,通常是4
--executor-cores 4 \
#一个core可以支配3-5G的内存。
--executor-memory 12G \
wordcount.py
为什么企业中都使用-yarn-cluster模式?
【了解】云服务版
####【了解】spark standalone的过程是怎样的?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrHxqLph-1665888837472)(面试指导.assets/1639207849909.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QclCOHy-1665888837472)(面试指导.assets/1639208231107.png)]
各个概念的层级关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S4CdiF9s-1665888837473)(面试指导.assets/1639208247272.png)]
####【重点】spark on Yarn的过程是怎样的?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rnn0lMGp-1665888837473)(面试指导.assets/1639209098721.png)]
Spark On Yarn 不需要启动Master和Worker进程。
standalone方式才有Master和Worker进程,跟Yarn没有任何关系。
standalone方式,不涉及到ApplicationMaster进程。
ApplicationMaster仅仅是SparkONYarn才有的概念。跟standalone没有任何关系。
核心设计要点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YoFA9oDa-1665888837474)(面试指导.assets/1637222951490.png)]
persist或cache【会】保留RDD的血缘关系,如果某个分区的数据丢失,那么可以借助【依赖关系】重新计算出这个分区的数据。
但是HDFS【不】保留依赖关系,因为即使一个分区的数据丢失损坏,那么也能方便的直接使用HDFS的另外【2】个副本。
广播变量:
解决的场景:
将Driver进程的共享数据发送给所有子节点Executor进程的每个任务中。
如果不用广播变量技术,那么Driver端默认会将共享数据分发到每个【task】中,造成网络分发压力大。
如果使用了广播变量技术,则Driver端将共享数据只会发送到每个【Executor】一份。Executor中的所有【task】都复用这个对象。
要保证该共享对象是可【可序列化】的。因为跨节点传输的数据都要是可序列化的。
在Driver端将共享对象广播到每个Executor:
val bc = sc.broadcast( 共享对象 )
在Executor中获取:
bc.value
累加器
集群中所有Executor对同一个变量进行累计操作。
Spark目前只支持累【加】操作。
有3种内置的累加器:【LongAccumulator】、【DoubleAccumulator】、【CollectionAccumulator】。
整数累加器使用方法
在Driver端定义整数累加器,赋初始值。
acc=sc.accumulator(0)
在Executor端每次累加1
acc+=1
或者acc.add(1)
综合案例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5VsXfFX6-1665888837480)(面试指导.assets/1637285577947.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQcDzPEM-1665888837480)(面试指导.assets/1637286287275.png)]
# -*- coding:utf-8 -*-
# Desc:This is Code Desc
from pyspark import SparkConf, SparkContext
import os
# 这里可以选择本地PySpark环境执行Spark代码,也可以使用虚拟机中PySpark环境,通过os可以配置
os.environ['SPARK_HOME'] = '/export/server/spark'
PYSPARK_PYTHON = "/root/anaconda3/bin/python"
# 当存在多个版本时,不指定很可能会导致出错
os.environ["PYSPARK_PYTHON"] = PYSPARK_PYTHON
os.environ["PYSPARK_DRIVER_PYTHON"] = PYSPARK_PYTHON
if __name__ == '__main__':
#创建上下文对象SparkContext
conf=SparkConf().setAppName('sharevalue').setMaster('local[*]')
sc=SparkContext(conf=conf)
#需求1-从海量数据中剔除黑名单
#1-定义海量数据形成RDD
rdd1=sc.parallelize(['zs','ls','ww','zl'],3)
#2-定义黑名单list
black_list=['zs','ls']
#3-将黑名单list从Driver端广播到各个Executor中
bc=sc.broadcast(black_list)
#4-从RDD中剔除黑名单的人员
def filter_black(x):
#在Executor中获取变量
black_list2=bc.value
if x in black_list2:
return False
else: return True
rdd2=rdd1.filter(filter_black)
#5-打印过滤后的人员
print('过滤后,',rdd2.collect())
#需求2-从海量数字中挑选带7的有几个,他们的平均数是多少
#1-定义RDD包含有1000个数字
rdd=sc.parallelize(range(1,1001))
#2-定义累加器-累计有多少个7
num7=sc.accumulator(0)
#3-定义累加器-累计带有7的数字的和
sum7=sc.accumulator(0)
#4-对RDD的每个元素进行判断
def find7(x):
global num7
global sum7
if '7' in str(x):
num7+=1
sum7+=x
rdd.foreach(find7)
print('7的个数:',num7.value)
print('他们的平均数:',sum7.value/num7.value)
package cn.itcast.creazybd
import org.apache.spark.sql.SparkSession
/**
* @program: spark_sz24
* @description
* @author: chenjia868
* @create: 2021-12-11 20:00
* */
object SparkSQLReadAndWrite {//SparkSQLReadAndWrite$
def main(args: Array[String]): Unit = {
val spark:SparkSession =SparkSession.builder().appName(this.getClass.getSimpleName.stripSuffix("$")).master("local[*]").getOrCreate()
spark.read.json("data/input/resources/people.json").show()
val df2=spark.read.option("sep",";").option("header",true).option("inferSchema",true).csv("data/input/resources/people.csv")
df2.printSchema()
df2.show()
spark.read.parquet("data/input/resources/users.parquet").show()
//将DataFrame保存为json
df2.write.mode("overwrite").json("data/output/json")
//将DataFrame保存为csv
df2.write.mode("overwrite").csv("data/output/csv")
//将DataFrame保存mysql
df2.write.format("jdbc")
.option("url","jdbc:mysql://localhost:3306/bigdata")
.option("user","root")
.option("password","123456")
.option("dbtable","person")
.mode("overwrite").save()
}
}
package cn.itcast.creazybd
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._
/**
* @program: spark_sz24
* @description
* @author: chenjia868
* @create: 2021-12-11 20:20
* */
object S2SparkSQLWordcount {
def main(args: Array[String]): Unit = {
val spark:SparkSession =SparkSession.builder().appName(this.getClass.getSimpleName.stripSuffix("$")).master("local[*]").getOrCreate()
//读取文件形成DataFrame
val df=spark.read.text("data/words.txt")
//用SQL风格做wordcount
//注册成临时视图
df.createOrReplaceTempView("words_t")
df.show()
spark.sql("set spark.sql.shuffle.partitions=4")
spark.sql(
"""
|select word,
| count(*) as cnt
|from
|(select
| explode( split(value," ") ) word
|from words_t) t
|group by word
|order by cnt desc
|""".stripMargin).show()
//用DSL风格做wordcount
import spark.implicits._
df.select(
explode( split($"value"," ") ).alias("word")
).groupBy("word")
.count()
.orderBy($"count".desc)
.show()
Thread.sleep(100000)
}
}
--要在IDEA中启动Spark数据源,需要后天启动
--HDFS
--Hive的metastore
--Spark的Thriftserver
create or replace temporary view test_tb(cid,name,score) as values
(1,'zs',60),
(1,'ls',70),
--(1,'qq',70),
(1,'ww',80),
(1,'zl',90),
(2,'aa',80),
(2,'bb',90);
set spark.sql.shuffle.partitions=4;
select * from test_tb;
select *,
--SQL中能触发shuffle的操作有 group by、join、partition by、distinct
--排序
row_number() over (partition by cid order by score) rn,
rank() over (partition by cid order by score) rk,
dense_rank() over (partition by cid order by score) drk,
--聚合
sum(score) over (partition by cid ) sum1,
--下面2句是等价的,因为【range between unbounded preceding and current row】可以省略
sum(score) over (partition by cid order by score range between unbounded preceding and current row ) sum2,
sum(score) over (partition by cid order by score ) sum3,
--计算比自己第一名的,包括自己,比自己高一名的,三人的总分sum4。
sum(score) over (partition by cid order by score rows between 1 preceding and 1 following) sum4,
--向前向后取
--获取比自己低一名的分数
lag(score) over (partition by cid order by score) lag1,
--获取比自己低2名的分数,如果找不到,就显示为0
lag(score,2,0) over (partition by cid order by score) lag2,
--获取比自己高一名的分数
lead(score) over (partition by cid order by score) lead1
from test_tb;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jnftpdwh-1665888837481)(面试指导.assets/1637292487066.png)]
1、Parser,第三方类库Antlr实现。将sql字符串切分成Token,根据语义规则解析成一颗AST语法树,称为Unresolved Logical Plan;
简单来说就是判断SQL语句是否符合规范,比如select from where 这些关键字是否写对。就算表名字段名写错也无所谓。
2、Unresolved Logical Plan经过Analyzer,借助于表的真实数据元数据schema catalog,进行数据类型绑定和函数绑定,解析为resolved Logical Plan;
简单来说就是判断SQL语句的表名,字段名是否真的在元数据库里存在。
3、Optimizer,基于各种优化规则(常量折叠,谓词下推,列裁剪), 将上面的resolved Logical Plan进一步转换为语法树Optimized Logical Plan。这个过程称作基于规则优化(Rule Based Optimizer) RBO。
简单来说就是把SQL调整一下,以便跑得更快。
4、query planner,基于planning,将逻辑计划转换成多个物理计划,再根据代价模型cost model,筛选出代价最小的物理计划。这个过程称之为CBO(Cost Based Optimizer)
上面2-3-4步骤合起来,就是Catalyst优化器。
5、最后依据最优的物理计划,生成java字节码,将SQL转换为DAG,对RDD操作。
2大优化
RBO:基于规则的优化,比如【常量折叠】,【谓词下推】,【列裁剪】。
常量折叠举例
select 1+1 as id
from table1
上面的会优化为
select 2 as id
from table1
会提前将【1+1】计算(折叠)成【2】,再赋给id列的每行,不用每行都计算一次1+1
谓词下推举例
select *
from table1 a
join table2 b on a.id=b.id
where a.age>20
and b.cid=1
会优化为
select *
from(select * from table1 where a.age>20) a
join (select * from table2 where b.cid=1) b on a.id=b.id
在子查询阶段就提前将数据进行【过滤】,后期join的数据量就大大【减小】。
列裁剪举例
select a.name,
a.age,
b.cid
from ( select * from table1 where a.age>20) a
join ( select * from table2 where b.cid=1) b on a.id=b.id
会优化为
select a.name,
a.age,
b.cid
from ( select id,name,age from table1 where a.age>20) a
join ( select id,cid from table2 where b.cid=1) b on a.id=b.id
提前将需要的列查询出来,其他不需要的列【裁剪掉】。
CBO:多种物理计划基于cost model,选取最优的执行耗时最少的那个物理计划
如何查看:dataframe.explain(true)或者 sql语句中使用【explain extended SQL语句】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dr1cZV6R-1665888837481)(面试指导.assets/1637293388179.png)]
面试中