#面试指导
面试的基础指导
如何boss直聘上筛选简历
-
如果看到“硕士”、“985、211院校”,这些大概率是硬性条件。如果条件不符,可以不投。
-
面试心态
-
开始的2-3次,预期不会通过
-
第3-5次,有面试的感觉,熟悉了企业的常规的面试流程和面试官的风格
-
第4次后,学会了应变,有信心去面对各种应聘的困难
-
最后一定能找到工作。只是时间早晚问题。
-
年龄小是优势,不要过于担心,互联网行业拥抱年轻人
-
我们的知识面足够广,主流的组件一个都不缺,而那些所谓的企业4-5年经验的工程师,经常有偏科的情况
-
不要排斥外包,可以通过外包进入一线大厂,接触大厂的需求。比如腾讯,阿里等。可以接触到他们的IT架构,开发模式,组织架构,管理制度。外包薪资普遍不低。
-
相对于一线互联网大厂,难度较大,各行业的头部公司的IT部门也不错。
-
先进公司再说,就算薪资不高也不要灰心,在职工作半年,努力积累技术,为第二次跳槽做准备。
-
大数据程序员=70%开发工程师+10%测试+10%运维+10%业务
初级中级工程师具备的技能
- 具备2-3门语言,java、scala、python
- 具备数仓的理论、数仓构建、分析。
- 具备多种大数据的存储,分析框架。HDFS-Hbase(熟悉)。hive、Spark、flink、 kafka
- 熟悉常用的数据结构,算法,准备排序和查找算法。
可能会手写的代码
- hive中的wordcount- split-explode-groupby-count
- SparkCore的wordcount- sc.textFile(文件)->flatMap-map-reduceByKey
- sparksql的wordcount(2种风格DSL和SQL)- split-explode-groupby-count
- flink的wordcount
- 排序算法
- 冒泡、选择、快速
- 了解时间复杂度
- 空间复杂度,不需要过于计较,空间管够
筛选职位的注意事项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28Upus20-1665888837467)(面试指导.assets/1639052814339.png)]
简历怎么写
-
注意事项
- 不要刻意按照某个公司的要求编写简历,因为公司太多,精力不够
- 按照项目经验去编写简历,跟面试官说清楚,讲明白。
- 不用贴照片,看技术不看颜值。
- 简历不要太花哨,要干净整洁
- 不要出现图书馆、OA系统等项目
- 有CET4 CET6 PMP证书的,可以标注。
-
专业技能
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IaDlBvAd-1665888837467)(面试指导.assets/1639054097496.png)]
-
项目描述
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLCREpvY-1665888837468)(面试指导.assets/1639054318360.png)]
-
简化后
该项目是基于用户在美客多网站产生的日志结合历史数据完成业务需求分析。帮助运营决策分析,(该项目为何而来)。该项目数据采集自网站的日志服务器,并落入HDFS,完成了大数据仓库的Hive的ODS层建设(项目使用什么技术来分析数据)。通过SparkSQL整合Hive分析了用户留存率,网站活跃,地区分布等指标(分析了哪些指标)。最终用Superset展示报表数据。
-
-
项目职责
面试前
- 为何平时学习不错,说话利索,但是面试中,讲不出话或者紧张
- 熟练度不够
- 面试准备时,光想不说。应该平时脱稿多说几次。
- 可以和同学结伴练习,互相指点毛病。
- 自我介绍时-(活泼,稳重,专业,干练,不要冷淡,)
- 最多2-3分钟。描述清楚姓名,来自哪里,毕业年限,毕业学校(如果学校不好,可以不说),年龄(如果年龄太大,可以不说)。即可。
- 是根据知识理论来复习,还是根据项目来复习。
- 建议根据项目来复习。项目中有实际的例子。记得更清楚。光记理论容易遗忘。在项目中发现疑问,可以去讲义和笔记中寻找解答
- 对项目进行简化取舍。只挑选1-2个模块,每个模块只挑选2-3个核心指标即可。
- 分清楚技术栈的次重点
- 重点的技术框架:Hive,Spark,Flink。存储框架有基础的HDFS。
- 调度和采集需要了解:Oozie,DolphinScheduler,Sqoop,Flume,Kettle
- 鉴别职位
- 有的JD,虽然谈到了大数据Hadoop等,但是可能只是给大数据部门开发配套的管理系统。主要是java或Oracle的开发。不要投递。
- 说话的禁忌
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6DJhzK2X-1665888837469)(面试指导.assets/1637205251374.png)]
面试中
-
项目讲解:
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwUyTKEE-1665888837469)(面试指导.assets/1637205417133.png)]
-
“你们的项目用了多少节点,数据量是多少,团队人数,一个Spark任务分配多少资源。”
- 我们的回答要经得起推敲
- 可以不明说具体的集群节点,数据量,等对方问到再说。
- 如果他问了,回答如下:
- 集群:有集群运维部门专门负责,我们用的是其中一个yarn的队列。
- 如果是小公司:20台服务器的集群。我们这个项目使用的资源是200个CPU,600G的内存。(yarn的cpu:内存的配比是1核:3~5G内存)。
- 如果是大公司:共用集团的集群800,总共有10000个cpu,CPU和内存是以队列的形式管理的。queue_理赔_01 、queue_精算_01 。我们这个项目使用的队列中有资源200个核,600G的内存。(yarn的【核:内存】的配比是【1核:3~5G内存】)
- 数据量
- 离线:加载的最大的表的数据量10亿条,600G大小。对大表有分区的优化。每条1kb。
- 实时数据量:日均4百万条,集中在白天的活跃时间,每条0.5kb,一天2G。
- 团队人数
- 公司有300人,大数据部门有20人。有3个项目团队,我们团队6人(负责人1个,测试2个,开发3个)
- 集群:有集群运维部门专门负责,我们用的是其中一个yarn的队列。
-
“你在工作中遇到了什么技术困难,怎么解决的”
-
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操作,耗时大大降低。
-
几种常见的架构
- 教育项目的批处理架构
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5JfbQBWF-1665888837469)(面试指导.assets/1637206651187.png)]
- 数据仓库的分层架构
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ij2YeNN0-1665888837470)(面试指导.assets/1637206992598.png)]
- 教育项目的批处理架构
面试后
- 要显示对公司有兴趣,关注。
- 如果对岗位和公司感兴趣,可以多问几次,说明自己的意愿。
- 及时询问反馈,并找就业老师沟通存在什么问题。
- 如果有多份offer,询问就业老师,进行取舍。
- 可以亡羊补牢,将自己回答不好的地方,再次查询最优答案,或者给出最佳SQL语句。并转交给面试官。
Spark高频面试题
Spark Base
####一道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 :企业中用的最多,【生产上线】使用。
- 可以提交java或scala写的Spark程序:spark-submit --master local[*] --class abc.Wc xxx.wc.jar
- 也可以提交python写的Spark程序:spark-submit --master local[*] xxx.py
-
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应用的运行方式有几种,分别是怎样的?
-
单机版
- 本地方式,local方式
- spark-submit --master local[n]
- n表示使用本机的n个【core】。每个core默认只运行一个线程。
- 如果n是*,表示使用本地的【所有的core】。
- 用来自测开发阶段。
-
集群版
-
【了解】Spark自带的资源管理器。
- standalone
- 需要启动一个【Master】和多个【Worker】的进程才能使用。
- 提交命令:spark-submit --master 【spark://node1:7077】
- –deploy-mode分为【client】和【cluster】。默认是【client】。
- standalone HA
- 因为standalone方式的Master进程只有【1】个。存在单点故障,所以增加了多个Master进程,做高可用。
- 需要启动2个以上节点的Master .
- 提交命令:spark-submit --master 【spark://node1:7077,node2:7077】
- 如果要关闭standalone HA资源管理集群,需要额外手动关闭node2机器的Master进程,stop-master.sh。
- standalone
-
【重点】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模式?
- 1、因为这种方式,使得Driver向Executor们的通信都在同一个集群的网段中,通信效率高。而反之yarn-client模式的Driver在提交的网段,Executor们在yarn集群的网段,2个网段可能不一样,通信效率低。
- 2、一般在企业中提交的机器就那么1-2台,如果大家都在同一台机器上用client模式运行各自的Spark应用,那么会造成所有的Driver进程都启动在这台机器上,造成这台机器的负担很重,造成所有的任务都受到影响。而随机的空闲的WorkerNode分散了各个Driver,负载均衡。
-
-
【了解】云服务版
- k8s
####【了解】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没有任何关系。
Spark Core
RDD的特点和5大属性是什么?
- 弹性:【CPU】和【内存】都是可以扩展。中间数据存在【内存】,如果内存不够,可以溢写到【磁盘】
- 分布式:【计算】是分布式多节点上的
- 数据集:一个很大的抽象容器,使用起来跟python集合一样简单,支持【函数式】编程。
核心设计要点
- 不可变:RDD的元素内部都不能改变,但是可以【转换】成新的RDD。
- 可分区:RDD划分成几个部分区域。
- 并行计算:每个分区被一个任务处理,各任务并行计算。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YoFA9oDa-1665888837474)(面试指导.assets/1637222951490.png)]
- 有一个分区列表:将【RDD】划分成合理的分区数。
- 计算函数:每个分区的数据上都有【如何计算他的函数】
- 依赖列表:RDD转换成新的RDD,也记录了依赖关系。
- 【可选】分区器:当RDD的元素是【key-value键值对】时,可以指定分区器,规定了如何按key来分组到不同的分区中。比如是Hash分区器
- 【可选】最佳位置:记录了计算的最佳位置(移动代码比移动数据更划算)比如HDFS的block位置。
Transformation算子
- 一个RDD调用transformation算子,会产生新的RDD,记录的是RDD的【转换关系】。
- transformation算子都是【延迟加载】计算的。
- 粗略的分为
- 简单的:【map、filter、flatMap】
- 分区操作:【mapPartitions】
- 重分区函数:【repartition, coalesce ,partitionBy】
- 聚合函数:【reduceByKey,foldByKey,aggregateByKey,reduce,fold,aggregate】。
- 关联函数:【join,leftOuterJoin】。
mapPartitions与map的区别是什么?
- map是对RDD的每个【元素】进行转换
- mapPartitions是对RDD的每个【分区】打包进行转换。
- 比如当需要跟mysql等RDBMS交互时,用【mapPartitions / foreachPartition】性能更高。可以为每个分区复用同一个JDBC连接来处理分区内的所有元素。
groupByKey与reduceByKey的区别是什么?
- groupByKey的shuffle的数据量【大】,容易造成子RDD的分区的内存【溢出】。如果做wordcount词频统计,那么需要继续手动mapValues才能得到结果。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mFkoH1is-1665888837475)(面试指导.assets/1637223886512.png)]
- reduceByKey有【2】阶段的聚合,性能快。在父RDD分区内做了【预聚合】,在子RDD的分区内再次聚合。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-skgK2jPT-1665888837475)(面试指导.assets/1637223898458.png)]
Action算子有哪些?
- 当一个计算链条遇到Action算子,才触发一个job
- Action算子是【立即eager】计算。
- reduce、collect、count、first、take、saveAsTextFile、foreach、foreachPartition、countByKey
- collect:是将各个Executor的结果拉取到Driver端。转换成Driver端单机集合,可能会引起Driver端OOM。
- count:返回RDD的元素个数。
- reduce:对所有元素聚合得到一个值,给Driver。
宽窄依赖与shuffle的关系是什么?
- 宽依赖:【有】shuffle,父RDD的一个分区会被子RDD的【多】个分区所依赖(或父RDD的一个分区的数据分发到了子RDD的多个分区中)。特点是下面的父RDD的分区的出去的线条有【多】条。
- 宽依赖算子有:reduceByKey,join,reparation,ByKey的都是。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQn1aeZH-1665888837476)(面试指导.assets/1637225503602.png)]
- 窄依赖:【没有】shuffle,父RDD的一个分区只会被子RDD的【1】个分区依赖,(或父RDD的一个分区的数据整个都进入到了子RDD的1个分区中)。特点是下面的父RDD的分区的出去的线条只有【1】条就行了,不能有分叉。
- 窄依赖算子有:【map、union、filter、flatMap,mapPartitions】。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sj02zkro-1665888837476)(面试指导.assets/1637225534710.png)]
RDD的依赖的作用是什么?
- 从加载源数据开始,到【Action】算子结束,这是一个计算链条。会生成一个【job】,一个job对应一个【DAG】。
- 自Action算子从后往前依次判断,如果遇到【shuffle】依赖算子(比如【reduceByKey】,【join】这些算子)就断开形成2个【stage】。继续往前,如果是【窄】依赖算子(比如【map】,【flatMap】,【filter】等算子)则加入到同一个【stage】中。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qf570ODX-1665888837477)(面试指导.assets/1637226118812.png)]
- 如下图,同一个stage内的多个RDD间都是【窄】依赖,分区数不变,可以对同一个分区内的多个窄依赖算子【合并】操作,形成pipeline(管道),每条数据都可以一次性计算完成,每个分区是一个【pipeline】,由一个【task】(线程、core)执行完成。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7SSlIvL5-1665888837477)(面试指导.assets/1637226149763.png)]
cache、persist、checkpoint的区别是什么?
- 三者的共同点:都是将RDD的逻辑上的抽象数据持久化成真正的数据到存储介质。
- cache:将RDD的逻辑上的抽象数据持久化到【内存】。
- 调用的就是persist(StorageLevel.【MEMORY_ONLY】)
- persist
- persist(缓存级别),支持多种缓存级别。
- MEMORY_ONLY或者DISK_ONLY,仅缓存到内存或磁盘。
- xxx_2 ,有2个副本,有利于数据恢复。
- xxx_SER , 将数据序列化存储,减小体积,方便传输。
- checkpoint
- 检查点。将数据保存到【HDFS】上。由于cache和persist持久化到内存或磁盘,内存会断电丢失,磁盘会烧坏。都是易失介质。但是HDFS是高可用介质。比上面的cache和persist更安全。
- 当程序退出时,cache和persist的数据,会自动清除,但是checkpoint的数据不会自动清除,需要手动清除。
- 血缘关系:persist或cache【会】保留RDD的血缘关系,如果某个分区的数据丢失,那么可以借助【血缘关系】重新计算出来。但是HDFS【不会】保留依赖关系,因为即使一个分区的数据丢失损坏,那么也能方便的直接使用HDFS的另外【2】个副本。
为何cache、persist需要保留血缘,而checkpoint会截断血缘?
persist或cache【会】保留RDD的血缘关系,如果某个分区的数据丢失,那么可以借助【依赖关系】重新计算出这个分区的数据。
但是HDFS【不】保留依赖关系,因为即使一个分区的数据丢失损坏,那么也能方便的直接使用HDFS的另外【2】个副本。
Spark的shuffle是什么?
- 早期的Spark1.2:HashShuffle机制
- 第一代HashShuffle
- 未经优化的HashShuffle,中间小文件个数=【上游task数量】*【下游task数量】,非常之多
- groupby和join都能触发shuffle。下图演示groupby 聚合的情况
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KopoH1Ow-1665888837478)(面试指导.assets/1639216200938.png)]
- 优化后的HashShuffle
- 优化后的HashShuffle,中间小文件个数=【Executor的个数】*【下游reduceTask的个数】,数目成倍减少。
- groupby和join都能触发shuffle。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nyFb809x-1665888837478)(面试指导.assets/1639216240490.png)]
- 优化后的HashShuffle,中间小文件个数=【Executor的个数】*【下游reduceTask的个数】,数目成倍减少。
- 第一代HashShuffle
- Spark1.2之后,现在的:SortShuffle机制
- 普通的SortShuffle
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bg1ATImC-1665888837479)(面试指导.assets/1637228564414.png)]
- 1-定义数据结构:如果是reduceByKey这种聚合类的shuffle算子,那么会选用【Map】数据结构,如果是join这种shuffle算子,那么会选用【Array】数据结构
- 2-申请的内存=当前的数据内存情况*2-上一次的内存情况
- 3-排序:在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行【排序】。
- 4-溢写磁盘:排序过后,会分批将数据写入磁盘文件。*默认的batch数量是10000条,也就是说,排序好的数据,会以每批1万条数据的形式分批写入磁盘文件。*
- 5-合并:在文件中的start offset与end offset。表示文件索引
- bypass的SortShuffle
- (无需排序)
- 当shuffle write task的数量小于等于【spark.shuffle.sort.bypassMergeThreshold】参数的值时(默认为【200】)
- 不能是【聚合类的shuffle算子】。
- reduceByKey是聚合类的shuffle算子,不走bypass机制。
- groupBykey不是聚合类的shuffle算子,可能会走bypass机制。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PmEqzM7T-1665888837479)(面试指导.assets/1637228603889.png)]
- 普通的SortShuffle
累加器Accumulator和广播变量如何使用?
-
广播变量:
-
解决的场景:
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EQKNkY0a-1665888837479)(面试指导.assets/1637286323514.png)]
-
将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)
Spark SQL
RDD、DataFrame的比较。
- RDD:
- 弹性分布式数据集
- 容器可以装任意类型的可序列化元素(支持泛型)
- RDD的缺点是无从知道每个元素的【内部的属性】信息。意思是下图不知道Person对象的姓名、年龄等。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l2lUmXl1-1665888837480)(面试指导.assets/1637286672055.png)]
- DataFrame
- 也是弹性分布式数据集,但是称为分布式表更准确。
- 每个元素不是泛型对象,而是Row对象。
- RDD可以转换为DataFrame
- 通过spark.createDataFrame(RDD[Row]) 转换为DF
- spark.createDataFrame ( RDD[元组或列表] , 自定义Schema信息 )
- RDD[集合]+toDF([指定列名])
- DataFrame=RDD-【泛型】+schema+方便的SQL操作+【catalyst引擎】优化
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqDOwMnT-1665888837481)(面试指导.assets/1637286704784.png)]
SparkSession如何读取各种文件?以及SparkSession将结果写出保存是如何操作的?
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()
}
}
请分别用DSL领域查询语言和SQL,进行wordcount。
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;
结合catalyst说明Spark SQL的执行流程。如何查看?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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语句】
hive和SparkSQL的关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dr1cZV6R-1665888837481)(面试指导.assets/1637293388179.png)]
- 解读1:Hive和其他MySQL,Oracle一样,作为一种数据源被SparkSQL读取。
- 解读2:经常会在SparkSQL中集成Hive,集成后,不管后期是用SparkSQL,还是使用原生的hive ,建表时都将表的元数据存在了某个地方比如mysql中,都在hdfs的目录保存具体数据。只是引擎快慢区别。hive和spark-sql 都是DBMS,(注意不是RDBMS)。DBMS是Database Management System。侧重“Management管理”。是一个轻量级的东西。而数据是在HDFS上的,是重量级的东西。hive和spark-sql 是2种风格的管理者,2者SQL方言大部分一样,少数语法不同。不管2个方言怎么样,最终都落实到了HDFS数据文件的增删改查上了。
Hive10道常见SQL题
面试中