rdd 创建
从集合创建
//底层调用 paralllize方法
sparkContext.makeRDD(List[1,2,3,4])
//paralllize 并行
sparkContext.paralllize(Seq[Int](1,2,3,4))
从文件创建
sparkContext.textFile(“path”) //以行为单位读取 不考虑数据来源
sparkContext.wholeTextFiles(“path”) //以文件为单位,读取到的为元组,第一个元素为文件路径
rdd 并行和分区
makeRDD()方法可以传递第二个参数 numSlices 设置分区数量
第二个参数可以不传递,那么 方法将使用默认值: defaultParallelism
defaultParallelism = scheduler.conf.getInt(“spark.default.parallelism”, totalCores)
spark 默认情况下从sparkConf 中获取配置参数:spark.default.parallelism,如果获取不到,那么使用属性 totalCores
textFile 取最小分区 defaultMinPartitions: Int = math.min(defaultParallelism, 2)
rdd 算子
转化算子
-
map 分区内的数据执行是有序的,不同分区数据计算是无序的
-
mapPartition 以分区为单位进行数据转化操作,但是会将整个分区的数据加载到内存中进行引用,处理完的数据不会释放,因为存在引用,数据量较大,内存较小时 可能导致内存溢出
-
mapPartitionsWithIndex() 以分区为单位对数据进行处理,在处理的同时可以获取当前分区的索引
-
flatMap 扁平映射
-
glom 将同一分区的数据直接转化为相同类型的内存数组,进行处理,数据分区不变
-
groupby 将数据根据指定的规则进行分组,分区默认不变,但数据会被打乱重新组合(重分区),这样的过程称为 shuffle
一个组的数据在一个分区中,但并不是一个分区中只有一个组的数据 -
filter 对数据按照指定的规则进行过滤筛选,符合规则的数据保留,不符合的丢弃,进行数据筛选后分区不变,可能导致分区内数据不均衡,可能出现数据倾斜
-
distinct 底层去重原理:
case _ => map(x => (x, null)).reduceByKey((x, _) => x, numPartitions).map(_._1)
- coalesce 指定分区数量 进行重分区,可以选择是否shuffle,若不进行shuffle 则不会打乱数据
rdd.coalesce(2,true)
-
repartition 重分区 底层使用coalesce 默认进行shuffle
-
sortBy 根据指定规则进行排序,默认升序,第二参数传递false 为降序
-
双value 操作
要求两个数据源的数据类型保持一致
// 交集
rdd1.intersection(rdd2)
//并集
rdd1.union(rdd2)
//差集
rdd1.subtract(rdd2)
-
partitionBy 根据指定的分区规则对数据进行重分区
-
reduceByKey 相同的key的数据进行value 聚合的操作
-
groupByKey 相同的key的数据分在一个组中,形成一个对偶元组 :元组中第一个元素就是key, 第二个元素就是相同key 对应的 value 集合
区别:reduceByKey 底层会先在分区内进行聚合(combine 预聚合),可以有效减少shuffle的数据量,进而提高shuffle性能,因此reduceBykey 性能会优于 groupByKey
- aggregateByKey 按照指定分区内和分区间的聚合规则,对数据进行聚合操作
rdd.aggregateByKey(0)((x, y) => math.max(x, y), _ + _)
第1个参数列表 的参数 表示初始值
第二个参数列表:第1个参数 分组内聚合的规则
第2个参数 分组间的规则
- 若分组内和分组间的聚合规则相同,可采用 foldByKey, 如上可写为:
rdd.foldByKey(0)(_ + _)
- combineByKey 可以指定分组内的数据为初始值,直接进行聚合,
接收3个参数,第1个参数:分组内第一次出现的key 的初始化
第1个参数:分组内聚合规则
第1个参数:分组间聚合规则
如下两个rddavg 的值完全相同,区别是aggregateByKey 指定一个(0,0)元组作为初始值,combineByKey 指定组内第一个key的数据的初始化规则
val rddavgTuple = rdd.aggregateByKey((0, 0))((t, v) => (t._1 + v, t._2 + 1), (t1, t2) => (t1._1 + t2._1, t1._2 + t2._2))
val rddavgTuple2 = rdd.combineByKey((_, 1),
(t:(Int,Int), v) => (t._1 + v, t._2 + 1),
(t1:(Int,Int), t2:(Int,Int)) => (t1._1 + t2._1, t1._2 + t2._2))
val rddavg1 = rddavgTuple.mapValues { case (sum, cnt) => {sum / cnt}}
val rddavg2 = rddavgTuple.mapValues { case (sum, cnt) => {sum / cnt}}
行动算子
- reduce 将数据源中的数据两两聚合
val rdd = sc.makeRDD(Seq[Int](1, 2, 3, 4, 5))
println(rdd.reduce(_ + _))
-
collect 会将不同的分区数据按照分区数据采集到driver 内存中,形成数组
-
count 获取数据源中数据的个数
-
first 获取数据源中的第一个
-
take(N) 获取取 N 条数据
-
takeOrdered(N) 排序后取 N 条
//val ints = rdd2.takeOrdered(3) //默认升序 取前三
val ints = rdd2.takeOrdered(3)(Ordering.Int.reverse) //降序 取前三
println(ints.mkString(","))
- aggregate 分区数据通过初始值和分区内数据进行聚合,然后再和初始值和分区间数据聚合
val rdd = sc.makeRDD(Seq[Int](1, 2, 3, 4, 5), 2)
val i = rdd.aggregate(0)(_ + _, _ * _)
//结果为 0
-
fold aggregate算子的简化 分区内和分区间的规则相同时使用fold
-
countByValue 计算数据源 相同 value 的 个数,返回 map
val value = sc.makeRDD(List("spark hadoop", "hbase spark", "spark test"))
val res = value.flatMap(_.split(" ")).countByValue()
res.foreach(println(_))
- countByKey 计算数据源 相同 key 的 个数,返回 map (数据源需为键值类型)
val value = sc.makeRDD(List(("a", 1), ("b", 2), ("a", 3)))
val count = value.countByKey()
count.foreach(println(_))