spark入门学习
1. map与 mapPartitions的异同
map与mapPartitions的作用相同,map是对每一个element应用func, mapPartitions是对每一个partition应用func. partition是指分区,对数据的切分,类似hadoop的split, 比如一份数据有100行,element是指每一行,如果切分为4个partition,则每个partition有25行。
因此map和mapPartitions的api不太一样, map对应的func为: func(<T> line), mapPartitions对应的func为: func(Iterable<T> lines),
比如:
rdd = sc.textFile('./example.txt')
map: rdd.map(lambda x: x.split(' '))
mapPartitions: rdd.mapPartitions(lambda x: [i.split(' ') for i in x])
两个的结果是一样的, 不过mapPartitions更高效, 比如func中要打开数据库连接, map为为一行维护一个连接,mapPartitions为每一个分区打开一个连接,因此造成的消耗更少。
2. mapPartitionsWithIndex 和 mapPartitions一样, 不过对应的func由 func(Itrater<T> lines) 变成了 func(int, Iterable<T>, lines), int是分区的索引。不考虑第一个参数的情况下,两个是一样的.
比如: rdd.mapPartitionsWithIndex(lambda x,y: [i.split(' ') for i in y]) 与上面的mapPartitions作用一样, x是分区的唯一索引
3. groupByKey, <K,V> => <K, Iterable<V>>, 相同的key,分组
比如:
rdd = sc.parallelize(['a','b', 'a'])
rdd = rdd.map(lambda x: (x, 1))
rdd2 = rdd.grouByKey()
rdd2.collect() => [('b', [1]), ('a', [1,1])]
4. reduceByKey, <K,V> => <K,W>
比如:
rdd = sc.parallelize(['a','b', 'a'])
rdd = rdd.map(lambda x: (x, 1))
rdd2 = rdd.reduceByKey(lambda x, y : x+y) =>[('a',2), ('b',1)], 相同key的value相加
5.sortByKey, <K,V>=><K,V>根据key排序
比如:
rdd = sc.parallelize(['a', 'b', 'c', 'f','a'])
rdd = rdd.map(lambda x: (x,1))
rdd2 = rdd.sortByKey() => [('a', 1), ('a', 1), ('b', 1), ('c', 1), ('f', 1)]
6.join <K,V> join <K,W> => <K, (V,W)> 跟数据库的类似
rdd = sc.parallelize(['a', 'b', 'c', 'f','a']),map(lambda x: (x, 1)) =》 [('a', 1), ('b', 1), ('c', 1), ('f', 1), ('a', 1)]
rdd2 = sc.parallelize(['A', 'B','C']).map(lambda x: (str.lower(x),x)) = 》[('a', 'A'), ('b', 'B'), ('c', 'C')]
rdd3 = rdd.join(rdd2) =>[('a', (1, 'A')), ('a', (1, 'A')), ('c', (1, 'C')), ('b', (1, 'B'))] key:f没了, 还有leftoutjoin和rightoutjoin
7. cogroup <K,V> <K,W> => <K, (Iterable<V>, Iterable<W>)>, 将每个rdd中相同key的元素合并,以上个例子的rdd为例:
rdd3 = rdd.cogroup(rdd2) = > [('a', ([1], ['A'])), ..., ('f', ([1], []))], 注意f在rdd中有,rdd2中没有, 合并后的value为 ([1], [])
8. cartesian 笛卡儿积
9. pipe 执行外部命令,比如shell, perl脚本等, rdd.pipe('grep a').collect() => [u"('a', 1)", u"('a', 1)"]
10. coalesce 和 repartition ,对rdd进行重新分区,参数<int numPartitions, boolean shuffle>, repartition 默认 shuffle为true, coalesce 默认为false
比如rdd原来由100个分区,可以利用以上方法,重新划分为10个分区, 若要划分为比原来分区数量大,比如1000个分区, shuffle必须为true。
参考:repartition(numPartitions:Int):RDD[T]和coalesce(numPartitions:Int,shuffle:Boolean=false):RDD[T]他们两个都是RDD的分区进行重新划分,repartition只
是coalesce接口中shuffle为true的简易实现,(假设RDD有N个分区,需要重新划分成M个分区)
1)、N<M。一般情况下N个分区有数据分布不均匀的状况,利用HashPartitioner函数将数据重新分区为M个,这时需要将shuffle设置为true。
2)如果N>M并且N和M相差不多,(假如N是1000,M是100)那么就可以将N个分区中的若干个分区合并成一个新的分区,最终合并为M个分区,这时可以
将shuff设置为false,在shuffl为false的情况下,如果M>N时,coalesce为无效的,不进行shuffle过程,父RDD和子RDD之间是窄依赖关系。
3)如果N>M并且两者相差悬殊,这时如果将shuffle设置为false,父子RDD是窄依赖关系,他们同处在一个Stage中,就可能造成spark程序的并行
度不够,从而影响性能,如果在M为1的时候,为了使coalesce之前的操作有更好的并行度,可以讲shuffle设置为true。
总之:如果shuff为false时,如果传入的参数大于现有的分区数目,RDD的分区数不变,也就是说不经过shuffle,是无法将RDDde分区数变多的。
在spark使用saveAsTextFile的时候,如果由5个分区,会生成5个文件,可以使用上面的先分区为1,在调用saveAsTextFile方法,则会只生成一个文件。
11.aggregate(zeroValue, seqOp, combOp, [numTasks]).aggregate函数将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。aggregate函数将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。
rdd = sc.parallelize(range(10))
rdd.aggregate((0,0), lambda x, y: (x[0]+y, x[1]+1), lambda x, y:(x[0]+y[0], x[1]+y[1]))=>(45,10) zeroValue为(0,0),首先进行seqOp操作, lambda x,y:(x[0]+y, x[1]+1), 第一次的时候x是zeroValue(0,0),y是rdd的第一个数,以此类推,
假设有两个分区,分别得到的结果是(10,5),(35,5) , comOp会将zeroValue依次和(10,5),(35,5)进行combine操作
12.aggregateByKey和aggregate一样,接受PairRDD
rdd = sc.parallelize([(1,2), (2,0), (4,3), (1,-1)])
rdd2 = rdd.aggregateByKey((0,0), lambda x,y:min(x,y), lambda x,y: min(x,y)) => [(1, -1), (2, 0), (4, 3)]

浙公网安备 33010602011771号