RDD 转换算子
map
将父类RDD的元素以此转换成新的RDD元素
scala> sc.parallelize(List(1,2,3,4,5),3).map(item=>item+2).collect()
flatMap
将集合的元素展开,并转换
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.collect()
filter
过滤掉集合中不满足要求的元素
scala> sc.parallelize(List(1,2,3,4,5),3).filter(item=>item % 2 == 0).collect()
mapPartitions
同map算子类似,但是不同的是该算子可以拿到父RDD的分区的整个数据集,在数据量不大的时候,可以使用。
scala> sc.parallelize(List("a","b","c"),3)
.mapPartitions(ites => for(i<-ites) yield (i,1))
.collect()
mapPartitionsWithIndex
同mapPartitions算子一样,只不过是在传入参数的时候传进去了父类RDD的分区数。
scala>sc.parallelize(List("a","b","c"),3)
.mapPartitionsWithIndex((index,values)=>for(i<-values) yield (index,i) )
.collect()
sample
使用给定的随机数生成器种子,在有或没有替换的情况下对数据的一小部分进行采样。
scala> sc.parallelize(List("a","b","c","d","e","f","g"),3).sample(true,0.9,1)
scala> sc.parallelize(List("a","b","c","d","e","f","g"),3).sample(true,0.9,1).collect()
scala> sc.parallelize(List("a","b","c","d","e","f","g"),3).sample(false,0.5,1).collect()
union
返回一个新数据集,其中包含源数据集和参数中元素的并集。
scala> var rdd1=sc.parallelize(Array(("张三",1000),("李四",100),("赵六",300)))
scala> var rdd2=sc.parallelize(Array(("张三",1000),("王五",100),("温七",300)))
scala> rdd1.union(rdd2).collect()
intersection
返回包含源数据集和参数中元素交集的新RDD。
scala> var rdd1=sc.parallelize(Array(("张三",1000),("李四",100),("赵六",300)))
scala> var rdd2=sc.parallelize(Array(("张三",1000),("王五",100),("温七",300)))
scala> rdd1.intersection(rdd2).collect()
distinct
返回包含源数据集的不同元素的新数据集。
scala> var rdd1=sc.parallelize(Array(("张三",1000),("李四",100),("赵六",300)))
scala> var rdd2=sc.parallelize(Array(("张三",1000),("王五",100),("温七",300)))
scala> rdd1.union(rdd2).distinct().collect()
groupByKey
在(K,V)对的数据集上调用时,返回(K,Iterable )对的数据集。注意:如果要对每个键执行聚合(例如总和或平均值)进行分组,则使用reduceByKey或aggregateByKey将产生更好的性能。默认情况下,输出中的并行级别取决于父RDD的分区数。您可以传递可选的numPartitions参数来设置不同数量的任务。
scala> sc.parallelize(List("a","b","a","c"),3)
.map((_,1))
.groupByKey(3)
.map(t=> (t._1,t._2.sum))
.collect()
reduceByKey
当调用(K,V)对的数据集时,返回(K,V)对的数据集,其中使用给定的reduce函数func聚合每个键的值,该函数必须是类型(V,V)=> V.groupByKey类似,reduce任务的数量可通过可选的第二个参数进行配置。
scala> sc.parallelize(List("a","b","a","c"),3)
.map((_,1))
.reduceByKey((v1,v2)=>v1+v2,3)
.collect()
aggregateByKey
当调用(K,V)对的数据集时,返回(K,U)对的数据集,其中使用给定的组合函数和中性“零”值聚合每个键的值。允许与输入值类型不同的聚合值类型,同时避免不必要的分配。与groupByKey类似,reduce任务的数量可通过可选的第二个参数进行配置。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
.collect()
sortByKey
当调用K实现Ordered的(K,V)对数据集时,返回按键升序或降序排序的(K,V)对数据集,如boolean升序参数中所指定。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
.sortByKey(true,3)
.collect()
sortBy
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
.sortBy(_._2,true,3)
.collect()
join
当调用类型(K,V)和(K,W)的数据集时,返回(K,(V,W))对的数据集以及每个键的所有元素对。通过leftOuterJoin,rightOuterJoin和fullOuterJoin支持外连接。
scala> var rdd1=sc.parallelize(Array(("001","张三"),("002","李四"),("003","王五")))
scala> var rdd2=sc.parallelize(Array(("001",("apple",18.0)),("001",("orange",18.0))))
scala> rdd1.join(rdd2).collect()
cogroup
当调用类型(K,V)和(K,W)的数据集时,返回(K,(Iterable ,Iterable ))元组的数据集。此操作也称为groupWith。
scala> var rdd1=sc.parallelize(Array(("001","张三"),("002","李四"),("003","王五")))
scala> var rdd2=sc.parallelize(Array(("001","apple"),("001","orange"),("002","book")))
scala> rdd1.cogroup(rdd2).collect()
cartesian
当调用类型为T和U的数据集时,返回(T,U)对的数据集(所有元素对)。
scala> var rdd1=sc.parallelize(List("a","b","c"))
scala> var rdd2=sc.parallelize(List(1,2,3,4))
scala> rdd1.cartesian(rdd2)
scala> rdd1.cartesian(rdd2).collect()
coalesce
将RDD中的分区数减少为numPartitions。过滤大型数据集后,可以更有效地运行操作。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2).sortBy(_._2,true,3)
.coalesce(1).getNumPartitions
repartition
随机重新调整RDD中的数据以创建更多或更少的分区并在它们之间进行平衡。这总是随机播放网络上的所有数据。
scala> var rdd1=sc.parallelize(List("a","b","c"),3)
.mapPartitionsWithIndex((index,values)=>for(i<-values) yield (index,i) )
scala> rdd1.collect()
scala> var rdd2=rdd1.repartition(3)
.mapPartitionsWithIndex((index,values)=>for(i<-values) yield (index,i))
scala> rdd2.collect()
RDD 动作算子
reduce(fun)
使用函数func(它接受两个参数并返回一个)来聚合数据集的元素。该函数应该是可交换的和关联的,以便可以并行正确计算。
scala> var rdd1=sc.parallelize(List("a","b","c"),3).reduce((v1,v2)=>v1+","+v2)
rdd1: String = a,c,b
collect
在驱动程序中将数据集的所有元素作为数组返回。在过滤器或其他返回足够小的数据子集的操作之后,这通常很有用。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.collect()
count
返回数据集中的元素数。
scala> var rdd1=sc.parallelize(List("a","b","c"),3).count()
rdd1: Long = 3
first
返回数据集的第一个元素(类似于take(1))。
scala> var rdd1=sc.parallelize(List("a","b","c"),3).count()
scala> var rdd1=sc.parallelize(List("a","b","c"),3).take(1)
scala> var rdd1=sc.parallelize(List("a","b","c"),3).first()
take
返回包含数据集的前n个元素的数组。
scala> var rdd1=sc.parallelize(List("a","b","c"),3).take(3)
rdd1: Array[String] = Array(a, b, c)
takeSample
返回一个数组,其中包含数据集的num个元素的随机样本,有或没有替换,可选地预先指定随机数生成器种子。
scala> var rdd1=sc.parallelize(List("a","b","c"),3).takeSample(true,5,1)
rdd1: Array[String] = Array(b, c, c, b, a)
takeOrdered
使用自然顺序或自定义比较器返回RDD的前n个元素。
scala> var rdd=sc.parallelize(Array(("a",3),("b",1),("c",4)),5)
scala> val s=new Ordering[(String, Int)]{
| override def compare(x: (String, Int),y:(String,Int)):Int={
| return -1*(x._2-y._2)
| }
| }
scala> rdd.takeOrdered(2)(s)
saveAsTextFile
将数据集的元素作为文本文件(或文本文件集)写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统的给定目录中。Spark将在每个元素上调用toString,将其转换为文件中的一行文本。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
.sortBy(_._2,true,3)
.map(t=>t._1+"\t"+t._2)
.saveAsTextFile("hdfs:///results/")
saveAsSequenceFile
将数据集的元素作为Hadoop SequenceFile写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统中的给定路径中.这可以在实现Hadoop的Writable接口的键值对的RDD上使用。在Scala中,它也可以在可隐式转换为Writable的类型上使用(Spark包括基本类型的转换,如Int,Double,String等)。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
.sortBy(_._2,true,3)
.saveAsSequenceFile("hdfs:///results-seq/")
saveAsObjectFile
使用Java序列化以简单格式编写数据集的元素,然后可以使用SparkContext.objectFile()加载。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
.sortBy(_._2,true,3)
.saveAsObjectFile("hdfs:///results-object/")
scala> sc.objectFile("hdfs:///results-object/").map((t:(String,Int)) => t).collect()
countByKey
仅适用于类型(K,V)的RDD。返回(K,Int)对的散列映射,其中包含每个键的计数。
scala> sc.textFile("hdfs:///demo/words")
.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
.countByKey()
foreach
在数据集的每个元素上运行函数func。这通常用于辅助工作,例如更新累加器或与外部存储系统交互。
scala> sc.objectFile("hdfs:///results-object/")
.foreach((t:(String,Int)) => println(t))
共享变量
通常,当在远程集群节点上执行传递给Spark操作(例如map或reduce)的函数时,如果这些function需要用到Driver中定义的变量,spark会将这些定义在Driver中的变量拷贝到所有的worker节点,并且这些变量的修改的值并不会传递回来给Driver定义的变量。这样看来通常跨任务的读写共享变量效率不高,但是,Spark确实为两种常见的使用模式提供了两种有限类型的共享变量:广播变量
和累加器
。
广播变量
通常情况下,当一个RDD的很多操作都需要使用driver中定义的变量时,每次操作,driver都要把变量发送给worker节点一次,如果这个变量中的数据很大的话,会产生很高的传输负载,导致执行效率降低。使用广播变量可以使程序高效地将一个很大的只读数据发送给多个worker节点,而且对每个worker节点只需要传输一次,每次操作时executor可以直接获取本地保存的数据副本,不需要多次传输。
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(SparkBroadcastDemo.getClass.getSimpleName())
val sc = new SparkContext(conf)
val userList = List(
"001,张三,28,0",
"002,李四,18,1",
"003,王五,38,0"
)
val genderMap = Map("0" -> "女", "1" -> "男")
val genderMapBC:Broadcast[Map[String, String]] = sc.broadcast(genderMap)
val userRDD = sc.parallelize(userList)
val retRDD = userRDD.map(info => {
val prefix = info.substring(0, info.lastIndexOf(",")) // "001,张三,28"
val gender = info.substring(info.lastIndexOf(",") + 1)
val genderMapValue = genderMapBC.value
val newGender = genderMapValue.getOrElse(gender, "男")
prefix + "," + newGender
})
retRDD.foreach(println)
sc.stop()
累加器
Spark提供的Accumulator,主要用于多个节点对一个变量进行共享性的操作。Accumulator只提供了累加的功能。但是确给我们提供了多个task对一个变量并行操作的功能。但是task只能对Accumulator进行累加操作,不能读取它的值。只有Driver程序可以读取Accumulator的值。
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(SparkAccumulatorDemo.getClass.getSimpleName())
val sc = new SparkContext(conf)
// 要对这些变量都*7,同时统计能够被3整除的数字的个数
val list = List(1, 2, 3, 4,6,9)
val listRDD:RDD[Int] = sc.parallelize(list)
var counter = 0
val counterAcc = sc.longAccumulator("count")
val mapRDD = listRDD.map(num => {
counter += 1
if(num % 3 == 0) {
counterAcc.add(1)
}
num * 7
})
mapRDD.foreach(println)
println("counter:" + counter)
println("counterAcc:" + counterAcc.value)
sc.stop()