第6次作业
1. 简述Spark的优点及其生态系统。
高速处理:Spark利用内存计算和优化的执行计划,能够实现比传统MapReduce更高效的数据处理。
容错性:Spark提供了弹性分布式数据集(RDD)作为基本抽象,通过RDD的容错性和恢复机制,保证了数据处理的稳定性。
多语言支持:Spark支持Scala、Java、Python和R等多种编程语言,使得开发者能够使用自己熟悉的语言进行开发。
生态系统
- Spark Core:Spark核心,提供底层框架和核心支持。
- BlinkDB:一个用于在海量数据上运行交互式SQL查询的大规模并行查询引擎,它允许用户通过权衡数据精度来提升查询响应时间,其数据的精度被控制在允许的误差范围内。
- SparkSQL: 可以执行SQL查询,包括基本的SQL语法和HiveQL语法。读取的数据源包括Hive表、Parquent文件、JSON数据、关系数据库(如MYSQL等)。
- Spark Streaming:流式计算。比如,一个网站的流量是每时每刻都在发生的,如果需要知道过去15分钟或一个小时的流量,则可以使用Spark Streaming来解决这个问题。
- MLBase:MLbase是Spark生态圈的一部分,专注于机器学习,让机器学习的门槛更低,让一些可能并不了解机器学习的用户也能方便地使用MLbase。
- Mlib:MLbase的一部分,MLlib是Spark的数据挖掘算法库,实现了一些常见的机器学习算法和实用程序。
- GraphX:图计算的应用在很多情况下处理的数据都是很庞大的,比如在移动社交上面的关系等都可以用图相关算法来进行处理和挖掘,但是如果用户要自行编写相关的图计算算法,并且要在集群中应用,那么难度是非常大的。而使用Spark GraphX就可以解决这个问题,它里面内置了很多的图相关算法。
2. 解释 Spark的几个基本概念:RDD、分区、Spark应用、作业、惰性求值、RDD窄依赖、宽依赖、RDD持久化。
- RDD(Resilient Distributed Dataset):弹性分布式数据集,是Spark中的基本数据抽象,代表了分布在集群中的不可变数据集合。
- 分区(Partition):RDD中的数据会被分割成多个分区,每个分区存储在集群的不同节点上进行并行处理。
- Spark应用:Spark应用是由Spark程序组成的,通常包括一个驱动程序(Driver Program)和多个执行器(Executors),驱动程序负责将任务分发给执行器进行处理。
- 作业(Job):作业是Spark中最高层次的执行单元,通常对应一个动作(Action)操作。
- 惰性求值(Lazy Evaluation):Spark中的转换操作并不会立即执行,而是等到遇到动作操作时才会触发实际的计算。
- RDD窄依赖(Narrow Dependency)和宽依赖(Wide Dependency):RDD之间的依赖关系分为窄依赖和宽依赖,窄依赖表示每个父RDD分区最多只被一个子RDD分区使用,宽依赖表示每个父RDD分区可能被多个子RDD分区使用。
- RDD持久化(RDD Persistence):RDD持久化可以将RDD缓存到内存或磁盘中,避免重复计算,提高计算性能。
3. 阅读、分析下列各程序段中各语句的功能,并给出运行结果。
(1)
val arr = Array(1,2,3,4,5,6)
val rdd = sc.parallelize(arr)
val sum = rdd.reduce(_ + _)
println(sum)
(2)
val x = sc.parallelize(List(1,2,3,4))
val x = x
.map(x => x * x)
print(x.collect().mkString(";"))
(3)
val x = sc.parallelize(
List("101 張三 95","102 李四 90","103 王五 96")
)
val y = x.map{ x => var t = x.split(" "); (t(0),t(1),t(2).toInt) }
y.filter(_._3 >= 95).map{x => (x._1,x._2)}.collect().foreach(println)
(4)
val x = sc.parallelize(1 to 9,3).map(x => (x % 3,x)).mapValues( v => v.toDouble)
val y = x.combineByKey(
(v:Double) => (v,1),
(c:(Double,Int),v:Double) => (c._1 + v, c._2 + 1),
(c1:(Double,Int),c2:(Double,Int)) => (c1._1 + c2._1,c1._2 + c2._2)
)
y.collect().foreach(print)
(5)
val goods = sc.parallelize(List("radio 30 50", "soap 3 60", "cup 6 50", "bowl 4 80"))
val goodsTup = goods.map { x => val splits = x.split(" "); (splits(0), splits(1).toDouble, splits(2).toInt) }
goodsTup.sortBy(_._1).collect().foreach(println)
goodsTup.sortBy(x => x._2, false).collect.foreach(println)
1. 计算数组元素的总和
// 创建一个包含数字 1 到 6 的数组
val arr = Array(1,2,3,4,5,6)
// 将数组转换为 Spark 的 RDD
val rdd = sc.parallelize(arr)
// 使用 reduce 操作计算 RDD 中所有元素的总和
val sum = rdd.reduce(_ + _)
// 打印总和
println(sum)
2. 对 RDD 元素进行平方操作
// 创建一个包含数字 1 到 4 的 RDD
val x = sc.parallelize(List(1,2,3,4))
// 使用 map 操作对每个元素进行平方运算
val x = x.map(x => x * x)
// 将 RDD 中所有元素收集到一个数组中,并使用分号连接打印
print(x.collect().mkString(";"))
3. 过滤学生成绩并打印姓名和学号
// 创建一个包含学生信息(学号、姓名、成绩)的 RDD
val x = sc.parallelize(
List("101 张三 95","102 李四 90","103 王五 96")
)
// 使用 map 操作将每行信息分割成学号、姓名、成绩三个元素,并转换为元组
val y = x.map{ x => var t = x.split(" "); (t(0),t(1),t(2).toInt) }
// 使用 filter 操作过滤成绩大于等于 95 的学生
y.filter(_._3 >= 95).map{x => (x._1,x._2)}.collect().foreach(println)
4. 使用 combineByKey 操作进行分组聚合
// 创建一个包含数字 1 到 9 的 RDD,并将其分成 3 个分区
val x = sc.parallelize(1 to 9,3).map(x => (x % 3,x)).mapValues( v => v.toDouble)
// 使用 combineByKey 操作进行分组聚合
val y = x.combineByKey(
// 创建初始值
(v:Double) => (v,1),
// 合并相同 key 的值
(c:(Double,Int),v:Double) => (c._1 + v, c._2 + 1),
// 合并不同分区的结果
(c1:(Double,Int),c2:(Double,Int)) => (c1._1 + c2._1,c1._2 + c2._2)
)
// 打印分组聚合后的结果
y.collect().foreach(print)
5. 对商品信息进行排序
// 创建一个包含商品信息(名称、价格、数量)的 RDD
val goods = sc.parallelize(List("radio 30 50", "soap 3 60", "cup 6 50", "bowl 4 80"))
// 使用 map 操作将每行信息分割成商品名称、价格、数量三个元素,并转换为元组
val goodsTup = goods.map { x => val splits = x.split(" "); (splits(0), splits(1).toDouble, splits(2).toInt) }
// 使用 sortBy 操作按照商品名称排序,并打印排序后的结果
goodsTup.sortBy(_._1).collect().foreach(println)
// 使用 sortBy 操作按照商品价格降序排序,并打印排序后的结果
goodsTup.sortBy(x => x._2, false).collect.foreach(println)