一. 单选题(共23题,100分)
1. (单选题)maven依赖应该加在哪个文件中?
- A. pom.xml
- B. log4j.properties
- C. src/main/scala.resource
- D. src/test/scala.resource
正确答案: A:pom.xml;
Maven 依赖应该添加在 pom.xml 文件中,这是 Maven 项目的核心配置文件。
解释:
- pom.xml (Project Object Model) 文件定义了项目的所有配置信息,包括项目依赖、插件、构建设置等
- 依赖通常在
<dependencies>
标签内定义,每个依赖使用<dependency>
标签,包含 groupId、artifactId 和 version 等信息 - 其他选项的用途:
- log4j.properties:日志配置文件
- src/main/scala.resource:资源文件目录,不是配置依赖的地方
- src/test/scala.resource:测试资源文件目录,同样不是配置依赖的地方
Maven 通过 pom.xml 文件管理项目的整个生命周期,包括依赖管理、构建过程和部署等。
2. (单选题)
1 val conf=new SparkConf()
2 .setMaster("local")
3 .setAppName("test")
4 var sc=new SparkContext(conf)
5 var data=sc.textFile("f:/00ppt/spark1/abc.txt")
如果要打包发布到spark集群里运行需要改哪些行,选出行号
- A. 2 3 4 5
- B. 2 5
- C. 3 5
- D. 2 4
正确答案: B:2 5;
解释:
当将Spark应用程序从本地开发环境打包发布到Spark集群运行时,需要修改以下行:
-
第2行:
.setMaster("local")
需要修改或删除- 在本地开发环境中,使用"local"表示在本地运行Spark
- 在集群环境中,应该删除这一行,让集群管理器(如YARN、Mesos或Spark Standalone)决定master
- 或者可以将其修改为集群地址,但通常在提交时通过spark-submit命令指定更灵活
-
第5行:
var data=sc.textFile("f:/00ppt/spark1/abc.txt")
需要修改- 本地文件路径"f:/00ppt/spark1/abc.txt"是Windows格式的本地路径
- 在集群环境中,应该使用分布式文件系统路径,如HDFS路径:"hdfs://namenode:8020/path/to/file"
- 或者使用集群可访问的共享存储路径
不需要修改的行:
- 第3行:
.setAppName("test")
应用名称可以保持不变 - 第4行:
var sc=new SparkContext(conf)
SparkContext创建方式不需要改变
3. (单选题)
var lst1=List("zs"->170,"ls"->175)
var rdd1=sc.parallelize(lst1)
rdd1.sortByKey().foreach(println(_))
输出结果是
-
A. (ls,175)
(zs,170)
-
B. (ls)
(zs)
-
C. (170)
(175)
-
D. (zs,170)
(ls,175)
正确答案: A:(ls,175) (zs,170) ;
解释:
让我们分析代码的执行过程:
-
首先创建了一个包含元组的List:
List("zs"->170, "ls"->175)
这里的 -> 操作符创建了两个二元组:("zs",170) 和 ("ls",175)
-
将List转换为RDD:
var rdd1 = sc.parallelize(lst1)
-
使用sortByKey()对RDD进行排序:
rdd1.sortByKey()
- sortByKey() 默认按照key进行升序排序
- 这里的key是字符串("zs"和"ls")
- 字符串按字典顺序排序,"ls"在字母表中排在"zs"前面
-
最后foreach打印每个元素:
foreach(println(_))
其他选项都是错误的:
- B只输出了key
- C只输出了value
- D没有经过排序
4. (单选题)
var lst1=List("zs"->170,"ls"->175)
sc.parallelize(lst1).sortBy(_____).foreach(println())
输出结果是
(zs,170)
(ls,175)
请填空
- A. x=>x
- B. _._2
- C. _._1
- D. x=>x.split("->")[1]
正确答案: B:_._2;
解释:
在这种情况下,sortBy
根据的是元组的 第二个元素(value) 来进行排序。具体来说:
lst1
是一个包含元组的列表:("zs" -> 170, "ls" -> 175)
,其中"zs"
和"ls"
是键(key),170
和175
是值(value)。- 使用
sortBy(_._2)
时,表示按照元组的第二个元素(即身高,170
和175
)进行排序。
为什么不是其他选项:
- A. x => x:这会按整个元组的顺序排序,不是按键或值,因此不符合要求。
- C. _._1 :这表示按元组的第一个元素(即key)排序,结果会是按字母顺序排序,输出会是:
(ls, 175) (zs, 170)
这和题目要求的输出不符。 - D. x => x.split("->")[1] :这是不正确的,因为
x
是元组,而不是字符串,所以不能使用split
。
5. (单选题)
哪种分区可能造成数据倾斜?1 Hash分区 2 Range分区 3 自定义分区
- A. 仅1
- B. 1,2,3
- C. 仅1 ,2
- D. 2,3
正确答案: B:1,2,3;
解释:
所有三种分区方式 (Hash分区、Range分区、自定义分区) 都可能导致数据倾斜,下面逐一说明:
- Hash分区: Hash分区是根据某个键的哈希值来决定数据分配的分区。数据倾斜可能发生的原因是:
- 如果某些键的分布不均匀,某些分区可能会得到非常多的数据,而其他分区则相对较少。
- 例如,如果某些键的出现频率很高,所有具有这些高频键的数据就会被分配到少数几个分区,造成负载不均衡。
- Range分区: Range分区按数据的范围将数据分配到不同的分区。数据倾斜发生的原因是:
- 如果数据在某些范围内非常集中,可能会导致某个分区的负载远大于其他分区。
- 比如,某个数值区间(如[1000, 2000])可能包含大量的数据,而其他区间则相对空闲,造成负载不均。
- 自定义分区: 自定义分区由用户定义分区规则,因此,如果用户设计的分区策略不合理,也可能导致数据倾斜。
- 例如,如果按照某些不均匀分布的字段进行分区(如按用户ID分区,而ID分布不均),某些分区可能包含大量数据,而其他分区几乎没有数据。
6. (单选题)
var a1=Array(1,2,3,9,8,7,4,6,5)
var range1=sc.parallelize(List(______________)).map((_,1))
var rdd1=sc.parallelize(a1)
rdd1.map((_,1)).partitionBy(new RangePartitioner(3,range1))
选择合适的内容填空使数据尽量不倾斜
- A. 1,3,6
- B. 1,9,4
- C. 2,8,6
- D. 3,6,9
正确答案: D:3,6,9;
解释:
这个问题涉及到使用 RangePartitioner 进行数据分区,目标是避免数据倾斜。让我们分析一下代码和各个选项:
var a1 = Array(1, 2, 3, 9, 8, 7, 4, 6, 5) var range1 = sc.parallelize(List(______________)).map((_, 1)) var rdd1 = sc.parallelize(a1) rdd1.map((_, 1)).partitionBy(new RangePartitioner(3, range1))
在这段代码中:
- 我们有一个数组
a1
,包含 9 个元素:1, 2, 3, 9, 8, 7, 4, 6, 5 - 需要创建一个 RDD
range1
,其中包含一些关键的分区边界值 - 然后使用
RangePartitioner
将rdd1
分成 3 个分区
RangePartitioner 的工作原理:
- RangePartitioner 会根据提供的样本数据(这里是
range1
)确定分区边界 - 它会尝试将数据均匀地分布在各个分区中
为了避免数据倾斜,我们需要选择能够将原始数据 a1
均匀分配到 3 个分区的边界值。
分析选项 D: 3,6,9:
- 如果选择 3, 6, 9 作为边界值,RangePartitioner 会创建以下分区:
- 分区 1: 小于或等于 3 的值 ------ 包含 1, 2, 3(共 3 个元素)
- 分区 2: 大于 3 且小于或等于 6 的值 ------ 包含 4, 5, 6(共 3 个元素)
- 分区 3: 大于 6 的值 ------ 包含 7, 8, 9(共 3 个元素)
这样,9 个元素被均匀地分配到 3 个分区中,每个分区包含 3 个元素,实现了最佳的负载均衡,避免了数据倾斜。
其他选项的问题:
- 选项 A (1,3,6): 分区 1 包含 1 个元素,分区 2 包含 2 个元素,分区 3 包含 6 个元素,不均衡
- 选项 B (1,9,4): 这些值不是有序的,不适合作为 RangePartitioner 的边界值
- 选项 C (2,8,6): 这些值也不是有序的,不适合作为边界值
7. (单选题)
自定义分区类的设计,按key的首字母分区,a-g在0区,h-n在1区,o-t在2区,u-z在3区,请填空
class MyPartitioner1(n:Int) extends Partitioner{
override def numPartitions: Int = __________
override def getPartition(key: Any): Int = {
var ch=key._______________
if(ch<='n')
if(ch<='g') 0
else 1
else
if(ch<='t') 2
else 3
}
}
-
A. n _.1
-
B. n _.2
-
C. n toString().charAt(0)
-
D. 4 element(0)
正确答案: C:n toString().charAt(0);
需求分析:
-
这是一个自定义分区器,根据key的首字母将数据分成4个区:
- a-g: 区0
- h-n: 区1
- o-t: 区2
- u-z: 区3
-
需要填写两个空:
- 第一个空:需要返回分区数
- 第二个空:需要获取key的首字母
解答分析:
-
第一个空
numPartitions
:- 根据分区逻辑,总共有4个分区(0,1,2,3)
- 虽然构造函数接收参数n,但这里实际分区数是固定的4个
- 应填
n
(保持构造函数参数的一致性)
-
第二个空
getPartition
方法中的代码:- 需要获取key的首字母
- key是Any类型,需要先转换为字符串
- 然后获取第一个字符
- 应填
toString().charAt(0)
选项分析:
A. n, _.1
- _.1 语法不正确,无法获取字符串首字母
B. n, _.2
- _.2 语法不正确,无法获取字符串首字母
C. n, toString().charAt(0)
- n 正确,表示分区数
- toString().charAt(0) 正确,可以获取任意key的首字母
D. 4, element(0)
- 4 虽然是实际分区数,但不符合构造函数参数设计
- element(0) 语法不正确
8. (单选题)
spark-core中计算中采用了惰性求值方案,其中行动操作有()
1 count() 2 collect() 3 take() 4 first() 5 foreach()
- A. 1,2,3,4,5
- B. 1,2,4
- C. 1,3,5
- D. 2,3,4,5
正确答案: A:1,2,3,4,5;
解释:
在Spark中,操作可以分为两类:
- 转换操作(Transformations):惰性操作,不会立即执行
- 行动操作(Actions):触发实际计算的操作
让我们分析每个选项中的操作:
-
count()
- 是行动操作
- 返回RDD中元素的数量
- 会触发实际计算
-
collect()
- 是行动操作
- 将RDD中的所有元素收集到驱动程序中
- 会触发实际计算
-
take()
- 是行动操作
- 返回RDD中的前n个元素
- 会触发实际计算
-
first()
- 是行动操作
- 返回RDD中的第一个元素
- 相当于take(1)
- 会触发实际计算
-
foreach()
- 是行动操作
- 对RDD中的每个元素执行指定操作
- 会触发实际计算
其他常见的行动操作:
- reduce()
- aggregate()
- fold()
- saveAsTextFile()
- countByKey()
- foreach()
转换操作(Transformations)示例:
- map()
- filter()
- flatMap()
- groupBy()
- union()
- intersection()
行动操作的特点:
- 会触发实际的计算
- 会返回结果给驱动程序
- 导致之前的所有转换操作被执行
惰性求值的好处:
- 优化执行计划
- 减少不必要的计算
- 提高效率
9. (单选题)
关于spark-core说法正确的有()
1 当需要对RDD进行reduceByKey和filter,应该先filter
2 当需要对RDD按key值聚合运算,可以用reduceByKey也可以用groupByKey
3 选项2中,用reduceByKey效率更高
4 可以通过持久化(缓存)机制避免重复计算的开销
- A. 1,2,3,4
- B. 1,2,3
- C. 2,3,4
- D. 1,3,4
正确答案: A:1,2,3,4;
让我们逐条分析这些说法:
-
"当需要对RDD进行reduceByKey和filter,应该先filter" - ✓正确
- 原因:
- filter操作会减少数据量
- 先执行filter可以减少后续reduceByKey处理的数据量
- 这符合Spark的优化原则:尽早减少数据量
- 原因:
-
"当需要对RDD按key值聚合运算,可以用reduceByKey也可以用groupByKey" - ✓正确
- 两者都可以实现按key聚合
- reduceByKey: 在map端先进行combine,再进行reduce
- groupByKey: 直接在reduce端进行分组
-
"用reduceByKey效率更高" - ✓正确
- 原因:
- reduceByKey在map端有预聚合(combine)操作
- 减少了网络传输的数据量
- 而groupByKey需要传输所有数据到reduce端
- 原因:
-
"可以通过持久化(缓存)机制避免重复计算的开销" - ✓正确
- 通过cache()或persist()方法实现
- 可以将RDD存储在内存或磁盘中
- 避免多次计算同一个RDD
- 特别适用于迭代计算场景
10. (单选题)
JSON 语法规则正确的是()
1 数据在key-value对中 2 数据由逗号分隔 3 大括号 {} 保存对象4 中括号 [] 保存数组,数组可以包含多个对象
- A. 1,2,3
- B. 1,2,3,4
- C. 2,3,4
- D. 1,3
正确答案: B:1,2,3,4;
让我们详细分析 JSON 的语法规则:
- 数据在 key-value 对中 ✓
- 格式:
"key": value
- 示例:
{ "name": "John", "age": 25 }
- 数据由逗号分隔 ✓
- 多个 key-value 对之间使用逗号分隔
- 数组元素之间使用逗号分隔
- 示例:
{ "name": "John", "age": 25, "city": "New York" }
- 大括号 {} 保存对象 ✓
- 对象以 { 开始,以 } 结束
- 示例:
{ "person": { "name": "John", "age": 25 } }
- 中括号 [] 保存数组,数组可以包含多个对象 ✓
- 数组以 [ 开始,以 ] 结束
- 示例:
{ "people": [ { "name": "John", "age": 25 }, { "name": "Mary", "age": 30 } ] }
11. (单选题)
val rdd1=spark.sparkContext.parallelize(
List(Person0("tom",21),Person0("zs",19),Person0("ls",20)) )
val df1=rdd1.toDF()
df1._______________________("person0")
val res1=spark.sql( "select * from person0 where age>20")
res1.show()
}
case class ________(name:String,age:Int)
- A. createSQLTable Person0
- B. querySQLString Person1
- C. createSQLTable Person
- D. createOrReplaceTempView Person0
正确答案: D:createOrReplaceTempView Person0;
为什么 createOrReplaceTempView
是正确的?
createOrReplaceTempView
用来将一个DataFrame
注册为一个临时视图或表,允许我们在 SQL 查询中使用它。- 这个临时视图只会在当前 Spark 会话中有效。
选项分析:
- A: createSQLTable Person0 :这个方法不存在,Spark没有提供名为
createSQLTable
的方法。 - B: querySQLString Person1 :同样,
querySQLString
不是 Spark 的有效方法。 - C: createSQLTable Person :同样,
createSQLTable
并不是一个有效的方法。 - D: createOrReplaceTempView Person0 :正确,
createOrReplaceTempView
是 Spark 中注册临时视图的方法。