sc.textFile(“里面放路径”) 这两个都是创建一个RDD
sc.parallelize(里面放集合)
val arr = Arry(1,2,3,4,5,6,7,8,9)
val rdd = sc.parallelize(arr)
rdd.partitions.length 这是查看有几个分区
用parallelize创建分区的时候不指定分区,分区的数量跟total-executor-cores指定几个核有关
分区数量决定了任务的并行度 并行度越高task越多
rdd.collect 提交结果 就会触发一次action 触发一次action就会有一个job
也可以在创建RDD的时候指定分区
val rdd = sc.parallelize(arr,100) 这边在第二个参数指定了有100个分区
也可以用makeRDD的方式创建RDD,当然也可以指定分区
val rdd = sc.parallelice(arr)
怎么进行分区的嫩
先totalSize(所有文件大小都加在一起)goalSize(指定的话就是1)不指定就是传进来的默认值2,所以如果不是指定的goalSize就是totalSize除以2,如果这个文件除以goalSize大于1.1就切
之前的对数据的map,split操作路径都可以不存在因为并没有真正的对数据进行操作,reduceBykey要路径存在因为要聚合从别的机器上抓取数据,sortBy会生成一个job,因为他要排序就要这道数据的信息,对数据进行采样构建一个新的特殊的分区器
记住:对RDD进行操作就是对RDD里的每一个分区进行操作,对RDD进行map本质上就是对RDD里每一个分区对应的迭代器进行map
RDD的算子的分类
Transformation: 即转换算子,调用转换算子会生成一个新的RDD, Transformation是 Lazy 的,不会触发job执行
Action: 行动算子,调用行动算子会触发job执行, 本质上是调用了sc.runJob方法, 该方法从最后一个RDD,根据
其依赖关系,从后往前,划分Stage,生成一个TaskSet
1.创建RDD的方法
(1)通过并行化方式,将Dirver端的集合转成RDD ,可以指定分区的数量
val rdd: RDD[Int] = sc.parallelize(arr)
rdd.partitions.length //查看分区数量
(2)从HDFS指定目录创建RDD ,也可以指定分区的数量
val lines: RDD[String] = sc.textFile("hdfs://linux01:8020/log")
rdd.partitions.length //查看分区数量
(2.1)从hdfs中读取数据分区数量 : 默认情况下 目录文件下所有文件的 totalSize(文件总大小)/NumSplits (切片数量) 得到 goalSize()
使用 文件大小/goalSize 结果大于1.1 就分为多个切片.
如果想要几个block块(文件)就有几个分区, 在创建rdd时指定计算goalSize的除数为1就可以了
val rdd1 = sc.textFile(“hdfs://linux01:8020/wc”,1)
DAG 有向无环图
srage
任务执行阶段
一个Stage对应一个TaskSet
一个TaskSet中的Task的数量取决于Stage中最后一个RDD分区的数量
dependency
依赖关系,指的是父RDD和子RDD之间的依赖关系
窄依赖:没有shfuffle产生,多个算子会被合并到一个Task中,即在一个pipeline中
宽依赖:有shuffle产生,是划分Stage的依据
JOB
触发一次Action形成一个完整的DAG,一个DAG对应一个job
一个job中有一到多个Stage,一个Stage对应一个TaskSet,一个TaskSet中有一到多个Task
Application
使用SparkSubmit提交的任务
一个Application中可以触发一到多次Action,触发一次Action形成一个DAG,一个DAG对应一个job
一个Appalication中可以有一到多个job
HashPartitioner源码分析
package cn.doit.spark.day02
/*
HashPartitioner(分区器)源码分析
*/
object HashPartitionerDemo {
def nonNegativeMod(x:Int , mod:Int): Int = {
//文件的hashcode值为x,mod为下游分区
val rawMod = x % mod
rawMod + (if (rawMod < 0) mod else 0)
}
def main(args: Array[String]): Unit = {
// val word = "spark" //1
// val word = "hadoop" //1
val word = "flume" //3
//index分到第几个区 4为下游分区
val index = nonNegativeMod(word.hashCode, 4)
println(index)
}
}
RDD算子之Transformation 转换算子, lazy的,调用后生成一个新的RDD
- 不产生Shuffle的
map filter
map filter
package org.apache.spark.day02
import org.apache.spark.rdd.MapPartitionsRDD
import org.apache.spark.{SparkConf, SparkContext}
/*
map filter
对RDD进行操作,其实是对RDD中每个分区对应的迭代器进行操作,迭代器会对每一个分区内的数据进行操作
*/
object MapPartitionsRDDDemo {
def main(args: Array[String]): Unit = {
//setMaster设置为本地模式
val conf = new SparkConf().setAppName("MapDemo").setMaster("local[*]")
//创建SparkContext
val sc: SparkContext = new SparkContext(conf)
//数组
val arr = Array(1,2,3,4,5,6,7,8,9)
//创建RDD 指定分区
val rdd1 = sc.parallelize(arr, 3)
//map 对RDD进行操作,其实是对RDD中的每个分区进行操作,是对RDD中每个分区对应的迭代器进行操作
// val rdd2 = rdd1.map(_ * 10)
// MapPartitionsRDD有包访问权限(private(this)),无法调用,可以伪装,
// 创建一个和路径和倒包一样的在里面就可以使用 org.apache.spark.day02.MapPartitionsRDDDemo
// val func = (a:Int) => a * 10
// val rdd2 = new MapPartitionsRDD[Int, Int](rdd1, (taskContext, index, iterator) => iterator.map(func))
//filter
val filtered = (a:Int) => a % 2 == 0
val rdd2 = new MapPartitionsRDD[Int, Int](rdd1, (_, _, iterator) => iterator.filter(filtered))
//将元素收集到一个数组中 打印 collect是Action算子
println(rdd2.collect().toBuffer)
//释放资源
sc.stop()
}
}
2 产生Shuffle的 在分布式计算中,将数据按照一定的计算逻辑(分区器),将具有相同规律的数据通过网络传输到指定的位置
reduceByKey groupByKey groupBy
reduceByKey
调用reduceByKey底层调用的是ShuffledRDD 特殊情况:调用reduceByKey不一定进行shuffle,如果前面的数据已经分好区了,
那就不需要在进行分区了(使用同样的分区器(列如都是Hashpartitioner),并且分区数量相同)
package cn.doit.spark.day02.demo02
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/*
reduceByKey 有shuffle
*/
object ReduceByKeyDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("ReduceByKeyDemo").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
val lines: RDD[String] = sc.textFile(args(0))
val wordAndOne: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1))
// val reduced: RDD[(String, Int)] = wordAndOne.reduceByKey(_ + _)
//第一个函数:该分区内第一次出现key对应的value进行运算
val f1 = (x:Int) => x
//第二个函数:在该分区内将key相同的value继续进行局部聚合
val f2 = (x:Int , y:Int) => x + y
//第三个函数全局聚合
val f3 = (a:Int , b:Int) => a + b
//reduceByKey底层调用的就是combineByKey,再底层就是调用的ShuffledRDD
//底层调用的是combineByKeyWithClassTag, 并在方法中中new的ShuffleRDD
val reduced = wordAndOne.combineByKey(f1, f2, f3)
//仅分区不排序,可以选择性的要不要排序
//val shuffledRDD: ShuffledRDD[String, Int, Int] = new ShuffledRDD[String, Int, Int]
//(wordAndOne, new HashPartitioner(wordAndOne.partitions.length))
// true是局部聚合,false是不局部聚合
//shuffledRDD.setMapSideCombine(true)
//聚合器,将三个参数聚合
//val aggregator = new Aggregator[String, Int, Int](f1, f2, f3)
//shuffledRDD.setAggregator(aggregator)
//val res = shuffledRDD.collect()
//println(res.toBuffer)
//将数据放进一个数组
val res = reduced.collect()
//打印
println(res.toBuffer)
}
}
groupByKey(分组) 底层也是先局部再全局 效率高于groupBy 推荐使用
package cn.doit.spark.day02.demo03
import org.apache.spark.rdd.{PairRDDFunctions, RDD, ShuffledRDD}
import org.apache.spark.{Aggregator, HashPartitioner, SparkConf, SparkContext}
import scala.collection.mutable.ArrayBuffer
object GroupByKeyDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("GroupByKeyDemo").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
val lines: RDD[String] = sc.textFile(args(0))
val wordAndOne: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map((_, 1))
// val grouped = wordAndOne.groupByKey()
// val grouped = new PairRDDFunctions[String, Int](wordAndOne).groupByKey()
//第一个参数, 把第一个key对应的value装进可变数组中
val f1 = (a:Int) => ArrayBuffer[Int](a)
//第二个参数,局部分组,将相同key分到一组,对应的value放进ArrayBuffer中
val f2 = (b:ArrayBuffer[Int], c:Int) => b += c
//第三个参数,全局分组,将每个分区的结果分组,key相同的分到一组 ,将数组合并到一起
val f3 = (x:ArrayBuffer[Int], y:ArrayBuffer[Int]) => x ++= y
val shuffledRdd: ShuffledRDD[String, Int, ArrayBuffer[Int]] = new ShuffledRDD[String, Int, ArrayBuffer[Int]](wordAndOne, new HashPartitioner(wordAndOne.partitions.length))
shuffledRdd.setMapSideCombine(false)
//聚合器
shuffledRdd.setAggregator(new Aggregator[String, Int, ArrayBuffer[Int]](f1,f2,f3))
val grouped = shuffledRdd.collect()
println(grouped.toBuffer)
// val tuples = grouped.collect()
// println(tuples.toBuffer)
}
}
groupBy(分组) 比groupByKey更加灵活,但是效率比groupByKey慢一点,推荐使用groupByKey
package cn.doit.spark.day02.demo03
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object GroupBy {
def main(args: Array[String]): Unit = {
System.setProperty("HADOOP_USER_NAME", "root")
//设置为本地模式,setMaster
val conf = new SparkConf().setAppName("GroupBy").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
val lst: Seq[String] = List("江苏省,徐州市,2000", "浙江省,宁波市,2005", "山东省,青岛市,3000", "江苏省,南京市,2010","江苏省,徐州市,2000")
val rdd1: RDD[String] = sc.parallelize(lst)
//groupBy底层也是调用的groupByKey 更加灵活,但是效率低于groupByKey
// val rdd2: RDD[(String, String, Double)] = rdd1.map(e => {
// val fields = e.split(",")
// (fields(0), fields(1), fields(2).toDouble)
// })
//
// val rdd3: RDD[(String, Iterable[(String, String, Double)])] = rdd2.groupBy(t => t._1)
// val tuples = rdd3.collect()
// println(tuples.toBuffer)
//groupByKey效率高于groupBy, 推荐使用
val rdd2: RDD[(String, (String, Double))] = rdd1.map(e => {
val fields = e.split(",")
(fields(0), (fields(1), fields(2).toDouble))
})
val rdd3: RDD[(String, Iterable[(String, Double)])] = rdd2.groupByKey()
val result = rdd3.collect()
println(result.toBuffer)
}
}
distinct(过滤) 局部去重再全局去重
package cn.doit.spark.day02.demo04
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object distinctDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("distinctDemo").setMaster("local[*]")
val sc = new SparkContext(conf)
val arr = Array(1,2,3,4,5,1,2,3,4,5,6,7,8,7)
val rdd1: RDD[Int] = sc.parallelize(arr, 2)
val result = rdd1.distinct()
println(result.collect().toBuffer)
// //将数据编成k,v
// val numAndNull: RDD[(Int,Null)] = rdd1.map(e =>(e,null))
//
// //去重
// val reduced = numAndNull.reduceByKey((a, _) => a)
//
// //整理
// val result = reduced.map(_._1)
//
// println(result.collect().toBuffer)
}
}