版权声明:本文为博主原创文章,未经博主允许不得转载!
欢迎访问:https://blog.csdn.net/qq_21439395/article/details/83591271
交流QQ: 824203453
- RDD编程API
- RDD算子
算子是RDD中定义的方法,分为转换(transformantion)和动作(action)。Tranformation算子并不会触发Spark提交作业,直至Action算子才提交任务执行,这是一个延迟计算的设计技巧,可以避免内存过快被中间计算占满,从而提高内存的利用率。
RDD拥有的操作比MR丰富的多,不仅仅包括Map、Reduce操作,还包括filter、sort、join、save、count等操作,并且中间结果不需要保存,所以Spark比MR更容易方便完成更复杂的任务。
RDD支持两种类型的操作:
转换(Transformation) 现有的RDD通过转换生成一个新的RDD。lazy模式,延迟执行。
转换函数包括:map,filter,flatMap,groupByKey,reduceByKey,aggregateByKey,union,join, coalesce等等。
动作(Action) 在RDD上运行计算,并返回结果给驱动程序(Driver)或写入文件系统。
动作操作包括:reduce,collect,count,first,take,countByKey以及foreach等等。
collect 该方法把数据收集到driver端 Array数组类型
所有的transformation只有遇到action才能被执行。
当触发执行action之后,数据类型不再是rdd了,数据就会存储到指定文件系统中,或者直接打印结果或者收集起来。
RDD操作流程示意:
RDD的转换与操作
wordcount示例,查看lazy特性。
只有在执行action时,才会真正开始运算,并得到结果或存入文件中。
-
-
- Transformation
-
RDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这种设计让Spark更加有效率地运行。
对RDD中的元素执行的操作,实际上就是对RDD中的每一个分区的数据进行操作,不需要关注数据在哪个分区中。
常用的Transformation:
转换 | 含义 |
map(func) | 返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成 |
filter(func) | 返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成 |
flatMap(func) | 先map,再flatten压平 |
union(otherDataset) | 对源RDD和参数RDD求并集后返回一个新的RDD |
intersection(otherDataset) | 对源RDD和参数RDD求交集后返回一个新的RDD |
subtract(otherDataset) | 求差集后返回新的RDD,出现在源rdd中,不在otherrdd中 |
distinct([numTasks])) | 对源RDD进行去重后返回一个新的RDD |
mapPartitions(func) | 类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U] |
mapPartitionsWithIndex(func) | 类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是 (Int, Interator[T]) => Iterator[U] |
sortBy(func,[ascending], [numTasks]) | 与sortByKey类似,但是更灵活 |
sortByKey([ascending], [numTasks]) | 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD |
join(otherDataset, [numTasks]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD |
cogroup(otherDataset, [numTasks]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的RDD |
cartesian(otherDataset) | 笛卡尔积 |
mapValues(func) | 在一个(K,V)的RDD上调用 |
groupBy (func, [numTasks]) | 根据自定义条件进行分组 |
groupByKey([numTasks]) | 在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD |
reduceByKey(func, [numTasks]) | 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置 |
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) | 针对分区内部使用seqOp方法,针对最后的结果使用combOp方法。 |
coalesce(numPartitions) | 用于对RDD进行重新分区,第一个参数是分区的数量,第二个参数是是否进行shuffle,可不传,默认不shuffle |
repartition(numPartitions) | 用于对RDD进行重新分区,相当于shuffle版的calesce |
groupBy的返回值类型:
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
T: 元素的类型 K : 指定的key
groupByKey
def groupByKey(): RDD[(K, Iterable[V])]
reduceByKey
优先选择reduceByKey, 语法更简洁
性能优越
reduceByKey会进行分区内聚合,再经过网络传输,发送到相对应的分区中。
sortBy既可以作用于RDD[K] ,还可以作用于RDD[(k,v)]
sortByKey 只能作用于 RDD[K,V] 类型上。
-
-
- Action
-
动作 | 含义 |
reduce(func) | 通过func函数聚集RDD中的所有元素 |
collect() | 在驱动程序中,以数组的形式返回数据集的所有元素 |
collectAsMap | 类似于collect。该函数用于Pair RDD,最终返回Map类型的结果。 |
count() | 返回RDD的元素个数 |
first() | 返回RDD的第一个元素(类似于take(1)) |
take(n) | 返回一个由数据集的前n个元素组成的数组 |
saveAsTextFile(path)
| 将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统 |
top(n) | 按照默认排序(降序) 取数据 |
takeOrdered(n, [ordering]) | 与top类似,顺序相反 默认是升序 |
countByKey() | 针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。 |
foreach(func) | 在数据集的每一个元素上,运行函数func进行更新。foreach,任务在executor中运行,打印信息也会在executor中显示 |
foreachPartition | 对分区进行操作 |
foreach和foreachPartition
foreachParititon 每次迭代一个分区,foreach每次迭代一个元素。
该方法没有返回值,或者Unit
主要作用于,没有返回值类型的操作(打印结果,写入到mysql数据库中)
在写入到mysql数据库的时候,优先使用foreachPartititon
* 结果 存入到 mysql中
* foreachPartition
* 1,map mapPartition 转换类的算子, 返回值
* 2, 写mysql 数据库的连接
* 100万 100万次的连接
* 200 个分区 200次连接 一个分区中的数据,共用一个连接
foreach和map的区别:
map有返回值,foreach没有返回值(Unit类型)
map是transformation,lazy执行,foreach是action算子,触发任务运行
处理的都是每一条数据。
rdd1.foreach(println) |
coalesce和repartition
def coalesce(numPartitions: Int, shuffle: Boolean = false)(implicit ord: Ordering[T] = null): RDD[T]
coalesce(n) 原来的分区中的数据,不会被分配到多个分区中,
将RDD分区的数量修改为numPartitions,常用于减少分区
第一个参数为重分区的数目,第二个为是否进行shuffle,默认为false
当需要调大分区时,必须设置shuffle为true,才能有效,否则分区数不变
随机重新shuffle RDD中的数据,并创建numPartitions个分区。这个操作总会通过网络来shuffle全部数据。常用于扩大分区
分区数调大调小,都会shuffle全部数据,是重量级算子
常用用法?
coalesce(10,true) = reparititon(10)
如果不需要数据的shuffle,减少或者合并分区,就使用coalesce(num)
如果需要数据的shuffle,或者需要扩大分区数量,优先使用repartition(num)
扩大分区,作用:提升并行度(业务逻辑比较复杂,需要提升并行度)
// 重分区的api
val f = (i:Int,it:Iterator[Int]) => it.map(t=> s"part:$i,values:$t") |
另外还有一类可以修改分区的方式:
在调用shuffle类的算子时,可以在参数中设置分区的数量:
def reduceByKey(func: (V, V) => V, numPartitions: Int): |
mapPartitions和mapPartitionsWithIndex
def mapPartitions[U](f: (Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false)(implicit arg0: ClassTag[U]): RDD[U]
该函数和map函数类似,只不过映射函数的参数由RDD中的每一个元素变成了RDD中每一个分区的迭代器。
如果在映射的过程中需要频繁创建额外的对象,使用mapPartitions要比map高效。
该方法,看上去是操作的每一条数据,实际上是对RDD中的每一个分区进行iterator,
mapPartitions( it: Iterator => {it.map(x => x * 10)})
mapPartitionsWithIndex
def mapPartitionsWithIndex[U](f: (Int, Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false)(implicit arg0: ClassTag[U]): RDD[U]
类似于mapPartitions, 不过提供了两个参数,第一个参数为分区的索引。
mapPartitionsWithIndex的func接受两个参数,第一个参数是分区的索引,第二个是一个数据集分区的迭代器。而输出的是一个包含经过该函数转换的迭代器。
val func = (index: Int, iter: Iterator[Int]) => {
iter.map(x => "[partID:" + index + ", val: " + x + "]")
}
val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 2)
rdd1.mapPartitionsWithIndex(func).collect
/**
|
collect方法:
不能直接把数据收集到driver段,然后再执行入库操作。
效率太低,容易引起dirver端崩溃了。OOM
版权声明:本文为博主原创文章,未经博主允许不得转载!
欢迎访问:https://blog.csdn.net/qq_21439395/article/details/83591271
交流QQ: 824203453