spark编程基本(二)-- RDD编程

RDD创建

从文件系统中加载数据创建RDD:

  1. Spark采用textFile() 方法来从文件系统中加载数据创建RDD

  2. 方法把文件的URI作为参数,这个URI可以是:

     1) 本地文件系统的地址
     2) 或者是分布式文件系统HDFS的地址
     3) 或者是Amazon S3的地址等
    

通过并行集合(数组)创建RDD:
可以调用SparkContext的parallelize方法,在Driver中一个已经存在的集合(数组)上创建。

val array = Array(1,2,3,4,5)
val rdd = sc.parallelize(array)

RDD操作

转换操作

  1. 对于RDD而言,每一个转换操作都会产生不同的RDD,供一个操作使用。
  2. 转换到的RDD是惰性求值的。不发生真正的计算。

基本的转化操作

函数名 目的
map() 将函数应用于RDD中的每个元素,将返回值构成新的RDD
flatmap() 将函数应用于RDD中的每个元素,将返回的迭代器的内容构成新的RDD,通常用来切分单词
filter() 返回一个通过传给filter()的函数的元素组成RDD
distinct() 去重
sample(withReplacement, fraction, [seed] 对RDD采样,以及是否替换
union() 生成一个包含两个RDD中所有元素的RDD
intersection() 求两个RDD共同的元素的RDD
subtract() 移除一个RDD中的内容
cartesian() 于另一个RDD的笛卡尔积
groupByKey 应用于(K, b)键值对的数据集时,返回一个新的(K, Iterable)形式的数据集
reduceByKey(func) 应用于(K, V)键值对的数据集时,返回一个新的(K, V)形式的数据集,其中的每个值是将每个Key传递到函数func中进行聚合

行动操作

真正触发计算的地方。

函数名 目的
collect() 返回RDD中的所有元素
count() RDD中的元素个数
countByValue() 各元素在RDD中出现的次数
take(num) 从RDD中返回num个元素
top(num) 从RDD中返回最前面的num个元素
takeOrdered(num) (ordering) 从RDD中按照提供的顺序返回最前面的num个元素
takeSample(withRepalcement, num, [seed]) 从RDD中返回任意一些元素
reduce(func) 并行整合RDD中所有的整数
fold(zero)(func) 和reduce()一样,但是需要提供初始值
aggreagte(zeroValue)(seqOp, comOp) 和Reduce()相似,但是通常返回不同类型的函数
foreach(func) 对RDD中的每个元素使用给定的函数

注意点:

  • collect() 要求所有数据都必须能一同放入单台机器的内存中。
  • take(n) 尝试只访问尽量少的分区,因此会得到一个不均衡的集合,返回元素的顺序与预期不一致。
  • top() 可以通过提供自己的比较函数,来提取前几个元素
  • foreach() 不将任何结果返回到驱动程序中,把数据发送到一个网络服务器中或数据库中。

持久化

spark中,RDD采用惰性求值得机制,每次遇到行动操作,都会从头开始执行计算,每次调用行动操作,都会触发一次从头开始的计算,这对于迭代操作而言,代价是很大的,迭代计算经常需要多次重复使用同一组数据。

可以通过持久化(缓存)机制避免这种重复计算的开销。
可以使用persist()方法对一个RDD标记为持久化。

persist()的圆括号内中包含的是持久化级别参数。

  • persist(MEMORY_ONLY):表示将RDD作为反序列化的对象存储于JVM中,如果内存不足,就要按照LRU原则替换缓存中的内存。
  • persist(MEMORY_AND_DISK)表示将RDD作为反序列化的对象存储在JVM中,如果内存不足,超出的分区将会被存放在硬盘上。
  • 可以使用unpersist()方法手动地把持久化的RDD从缓存中移除。
  • 一般而言,使用cache()方法时,会调用persist(MEMORY_ONLY)

分区

分区原则:
RDD分区的一个分区原则是使得分区的个数尽量等于集群中的CPU核心数目。

默认分区数目:
对于不同的Spark部署模式而言,都可以通过设置spark.default.parallelism这个参数的值,来配置默认的分区数目。

手动设置分区:

  1. 创建RDD时:在调用textFile和parallelize方法时候手动指定分区个数,sc.textFile(path, partitionNum)
  2. 通过转换操作得到RDD时:直接调用repatition方法即可(RDD数据量在经过转换操作后变小,无需过多的分区)

打印元素:

  • 单机模式下打印输出到屏幕上:rdd.foreach(println)或rdd.map(println)
  • 为了能够把所有worker节点上的打印输出信息也显示到Driver Program中,可以使用collect()方法:rdd.collect().foreach(println)。但是如果数据量过大,可能会导致内存溢出,采用rdd.take(100).foreach(println)。

Pair RDD的创建

从文件中加载

使用map()函数创建:

val lines = sc.textFile("file:///usr/local/spark/mycode/pairrdd/word.txt")

val pairRDD = lines.flatMap(line => line.split(" ").map(word => (word.1))

pairRDD.foreach(println)

通过并行集合(数组)创建RDD

val list = List("Hadoop","Spark","Hive")

val rdd = sc.parallelize(list)

val pairRDD = rdd.map(word => (word,1))

pairRDD.foreach(println)

常用的键值对

RDD转换操作

reduceByKey

reduceByKey(func)的功能是,使用func函数合并具有相同键的值

scala> pairRdd.collect().foreach(println)
(Hadoop,1)
(Spark,1)
(Hive,1)
(Spark,1)
scala> pairRdd.reduceByKey((a,b) => a+b).collect().foreach(println)
(Hive,1)
(Spark,2)
(Hadoop,1)

gourpByKey

groupByKey()按key值分组,生成一个值的列表。

scala> pairRDD.groupByKey()
res1: org.apache.spark.rdd.RDD[(String, Iterable[Int])] = ShuffledRDD[2] at groupByKey at <console>:31

reduceByKey和groupByKey的区别

scala> val words = Array("one", "two", "two", "three", "three", "three")
words: Array[String] = Array(one, two, two, three, three, three)

scala> val wordPairRdd = sc.parallelize(words).map(word => (word, 1))
wordPairRdd: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[4] at map at <console>:26

scala> val wordCountsWithReduce = wordPairRdd.reduceByKey(_+_)
wordCountsWithReduce: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[5] at reduceByKey at <console>:28                                                       ^

scala> val wordCountsWithGroup = wordPairRdd.groupByKey().map(t => (t._1, t._2.sum))
wordCountsWithGroup: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[7] at map at <console>:28

在这里插入图片描述
在这里插入图片描述
因为groupByKey在传输前不进行合并操作,所以通信开销比reduceByKey大。

keys、values和sortByKey

keys只会把pair RDD中的key返回形成一个新的RDD。
values只会把pair RDD中的value返回形成一个新的RDD。
sortByKey()的功能是返回一个根据键排序的RDD,默认为升序排序,参数为false时,降序
sortBy()根据所给参数决定根据键或值排序。

scala> val d1 = sc.parallelize(Array(("c", 8), ("b",25), ("c", 17), ("a", 42), ("b", 4),("d", 9),("e", 17), ("c", 2), ("f", 29), ("g", 21), ("b", 9)))
d1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[15] at parallelize at <console>:24

scala> d1.reduceByKey(_+_).sortByKey(false).collect
res3: Array[(String, Int)] = Array((g,21), (f,29), (e,17), (d,9), (c,27), (b,38), (a,42))

scala> val d2 = sc.parallelize(Array(("c", 8), ("b",25), ("c", 17), ("a", 42), ("b", 4),("d", 9),("e", 17), ("c", 2), ("f", 29), ("g", 21), ("b", 9)))
d2: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[8] at parallelize at <console>:24

scala> d2.reduceByKey(_+_).sortBy(_._2, false).collect
res2: Array[(String, Int)] = Array((a,42), (b,38), (f,29), (c,27), (g,21), (e,17), (d,9))

mapValues和join

mapValues(func)
对键值对RDD中的每个value都应用一个函数

join
表示内连接。对于给定的两个输入数据集,只有在两个数据集中都存在key才会被输出。最终得到一个(K, (V1, V2))类型的数据集

共享变量

Spark中的两个重要抽象是RDD和共享变量。
当Spark在集群的多个不同节点的多个任务上运行一个函数时,它会把函数中涉及到的每个变量在每个任务上都生成一个副本。
但是,有时候,需要在多个任务之间共享变量,或者在任务Task和任务控制节点Driver Program之间共享变量。
为了满足这种需求,Spark提供了两种类型的变量:广播变量和累加器。
广播变量用来把变量在所有节点的内存之间进行共享。
累加器则支持在不同节点之间进行累加计算。

广播变量:

  • 在每个机器上缓存一个只读的变量。
  • Spark的“行动”操作会跨越多个阶段,对每个阶段内的所有任务所需要的的公共数据,Spark都会自动进行广播。
//创建广播变量
val.broadcastVar = sc.broadcast(Array(1,2,3))
broadcastVar.value //获取广播值

累加器:

  • 累加器是仅仅被相关操作累加的变量,通常可以用来实现计数器和求和。
  • 一个数值型的累加器,可以通过调用SparkContext.longAccumulator()或者SparkContext.doubleAccumulator()来创建运行在集群中的任务,就可以使用add方法来把数值累加到累加器上,但是,这些任务只能做累加操作,不能读取累加器的值只有任务控制节点可以使用value方法来读累加器的值。
val accum = sc.longAccumulator("my accumulate")
val list = sc.parallelize(Array(1,2,3,4,5)).foreach(x => accum.add(x))
accum.value
发布了21 篇原创文章 · 获赞 0 · 访问量 397

猜你喜欢

转载自blog.csdn.net/leemusk/article/details/103459135