spark数据处理练习题番外篇【上】

一. 单选题(共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集群运行时,需要修改以下行:

  1. 第2行:.setMaster("local") 需要修改或删除

    • 在本地开发环境中,使用"local"表示在本地运行Spark
    • 在集群环境中,应该删除这一行,让集群管理器(如YARN、Mesos或Spark Standalone)决定master
    • 或者可以将其修改为集群地址,但通常在提交时通过spark-submit命令指定更灵活
  2. 第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) ;

解释:

让我们分析代码的执行过程:

  1. 首先创建了一个包含元组的List:

    List("zs"->170, "ls"->175)

    这里的 -> 操作符创建了两个二元组:("zs",170) 和 ("ls",175)

  2. 将List转换为RDD:

    var rdd1 = sc.parallelize(lst1)

  3. 使用sortByKey()对RDD进行排序:

    rdd1.sortByKey()

    • sortByKey() 默认按照key进行升序排序
    • 这里的key是字符串("zs"和"ls")
    • 字符串按字典顺序排序,"ls"在字母表中排在"zs"前面
  4. 最后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),170175 是值(value)。
  • 使用 sortBy(_._2) 时,表示按照元组的第二个元素(即身高,170175)进行排序。

为什么不是其他选项:

  • 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分区、自定义分区) 都可能导致数据倾斜,下面逐一说明:

  1. Hash分区: Hash分区是根据某个键的哈希值来决定数据分配的分区。数据倾斜可能发生的原因是:
  • 如果某些键的分布不均匀,某些分区可能会得到非常多的数据,而其他分区则相对较少。
  • 例如,如果某些键的出现频率很高,所有具有这些高频键的数据就会被分配到少数几个分区,造成负载不均衡。
  1. Range分区: Range分区按数据的范围将数据分配到不同的分区。数据倾斜发生的原因是:
  • 如果数据在某些范围内非常集中,可能会导致某个分区的负载远大于其他分区。
  • 比如,某个数值区间(如[1000, 2000])可能包含大量的数据,而其他区间则相对空闲,造成负载不均。
  1. 自定义分区: 自定义分区由用户定义分区规则,因此,如果用户设计的分区策略不合理,也可能导致数据倾斜。
  • 例如,如果按照某些不均匀分布的字段进行分区(如按用户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))

在这段代码中:

  1. 我们有一个数组 a1,包含 9 个元素:1, 2, 3, 9, 8, 7, 4, 6, 5
  2. 需要创建一个 RDD range1,其中包含一些关键的分区边界值
  3. 然后使用 RangePartitionerrdd1 分成 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);

需求分析:

  1. 这是一个自定义分区器,根据key的首字母将数据分成4个区:

    • a-g: 区0
    • h-n: 区1
    • o-t: 区2
    • u-z: 区3
  2. 需要填写两个空:

    • 第一个空:需要返回分区数
    • 第二个空:需要获取key的首字母

解答分析:

  1. 第一个空 numPartitions

    • 根据分区逻辑,总共有4个分区(0,1,2,3)
    • 虽然构造函数接收参数n,但这里实际分区数是固定的4个
    • 应填 n(保持构造函数参数的一致性)
  2. 第二个空 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中,操作可以分为两类:

  1. 转换操作(Transformations):惰性操作,不会立即执行
  2. 行动操作(Actions):触发实际计算的操作

让我们分析每个选项中的操作:

  1. count()

    • 是行动操作
    • 返回RDD中元素的数量
    • 会触发实际计算
  2. collect()

    • 是行动操作
    • 将RDD中的所有元素收集到驱动程序中
    • 会触发实际计算
  3. take()

    • 是行动操作
    • 返回RDD中的前n个元素
    • 会触发实际计算
  4. first()

    • 是行动操作
    • 返回RDD中的第一个元素
    • 相当于take(1)
    • 会触发实际计算
  5. foreach()

    • 是行动操作
    • 对RDD中的每个元素执行指定操作
    • 会触发实际计算

其他常见的行动操作:

  • reduce()
  • aggregate()
  • fold()
  • saveAsTextFile()
  • countByKey()
  • foreach()

转换操作(Transformations)示例:

  • map()
  • filter()
  • flatMap()
  • groupBy()
  • union()
  • intersection()

行动操作的特点:

  1. 会触发实际的计算
  2. 会返回结果给驱动程序
  3. 导致之前的所有转换操作被执行

惰性求值的好处:

  1. 优化执行计划
  2. 减少不必要的计算
  3. 提高效率

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;

让我们逐条分析这些说法:

  1. "当需要对RDD进行reduceByKey和filter,应该先filter" - ✓正确

    • 原因:
      • filter操作会减少数据量
      • 先执行filter可以减少后续reduceByKey处理的数据量
      • 这符合Spark的优化原则:尽早减少数据量
  2. "当需要对RDD按key值聚合运算,可以用reduceByKey也可以用groupByKey" - ✓正确

    • 两者都可以实现按key聚合
    • reduceByKey: 在map端先进行combine,再进行reduce
    • groupByKey: 直接在reduce端进行分组
  3. "用reduceByKey效率更高" - ✓正确

    • 原因:
      • reduceByKey在map端有预聚合(combine)操作
      • 减少了网络传输的数据量
      • 而groupByKey需要传输所有数据到reduce端
  4. "可以通过持久化(缓存)机制避免重复计算的开销" - ✓正确

    • 通过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 的语法规则:

  1. 数据在 key-value 对中 ✓
  • 格式:"key": value
  • 示例:

{ "name": "John", "age": 25 }

  1. 数据由逗号分隔 ✓
  • 多个 key-value 对之间使用逗号分隔
  • 数组元素之间使用逗号分隔
  • 示例:

{ "name": "John", "age": 25, "city": "New York" }

  1. 大括号 {} 保存对象 ✓
  • 对象以 { 开始,以 } 结束
  • 示例:

{ "person": { "name": "John", "age": 25 } }

  1. 中括号 [] 保存数组,数组可以包含多个对象 ✓
  • 数组以 [ 开始,以 ] 结束
  • 示例:

{ "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 中注册临时视图的方法。
相关推荐
雨中飘荡的记忆8 小时前
LangChain4j 实战指南
java·langchain
okseekw8 小时前
Java 中的方法:从定义到重载的完整指南
java
雨中飘荡的记忆8 小时前
深入理解设计模式之适配器模式
java·设计模式
用户84913717547168 小时前
生产级故障排查实战:从制造 OOM 到 IDEA Profiler 深度破案
java·jvm
越努力越幸运5088 小时前
git工具的学习
大数据·elasticsearch·搜索引擎
雨中飘荡的记忆8 小时前
深入理解设计模式之装饰者模式
java·设计模式
不会写程序的未来程序员8 小时前
详细的 Git 操作分步指南
大数据·git·elasticsearch
雨中飘荡的记忆8 小时前
秒杀系统设计与实现
java·redis·lua
小坏讲微服务9 小时前
Spring Cloud Alibaba 整合 Scala 教程完整使用
java·开发语言·分布式·spring cloud·sentinel·scala·后端开发
Kiri霧9 小时前
Scala 循环控制:掌握 while 和 for 循环
大数据·开发语言·scala