Spark Data Partitioning - Spark数据分区

一般来说,在分布式的编程中,通信是非常昂贵的,所以最大的提高性能的办法就是最小的减少网络之间的通信。Partitioning并不是在所有的应用中都是有用的,例如如果你只是对一个RDD数据全部扫描一次,这个就没有必要做partitioning操作。

partitioning操作一般是当你的数据集在一个面向key的操作中需要重复使用多次,例如joins。

Spark的partitioning在所有的RDDS pair都是存在的,即使spark没有显式的给每个key分配worker node。

例如:假设我们有一个非常大的userData RDD(UserId,UserInfo) paris,UserInfo里面包含了一系列用户订阅的主题。然后我们有另一个links RDD(UserId,LinkInfo) pairs,这个RDD保存的是用户点击了哪些链接。我们的应用就是得到哪些用户点击了哪些链接,完成这个功能我们可以把这个2个RDD使用join()方法来得到。

上面使用join()当然可以完成这个功能,但是效率会非常低,默认join()操作会hash所有的数据集的key,然后把相同元素的key通过网络发送到某个机器上,然后在该机器上进行join操作。

这里写图片描述

优化上面的问题我们可以使用partitionBy()这个函数对userData进行操作。当我们使用这个函数,spark就知道我们的userData数据已经是 hash-partitioned了。所以当我们使用 userData.join(events)的时候,Spark只会打乱 events RDD,然后把每个events的UserId发送到包含相应的UserId的userData上的机器去。

这个优化的结果就是:我们的大的数据集userData不需要对key进行hash和shuffe了,只需要等待events数据集中的key通过网络来找我们的userData中相同的key即可。

# 使用方法,这里把下面的数据分配到了4个partition上面
ts1 = sc.parallelize({(1, 2), (1, 10), (3, 30), (2, 20)})
d = ts1.partitionBy(4).persist()

这里写图片描述

注:partitionBy()是一个Transformation操作,所以它会返回一个新的RDD,并不会改变原始的RDD。RDD一旦被创建就不会被改变。因此我们可以persist这个partitionBy()后的RDD。partitionBy(100)代表有100个partitions,它是控制有多少个parallel task在这个RDD上进行操作。一般来说,它的值至少要与你的cluster机器的核数一样大。

如果没有使用persist(),那么后面的对这个RDD的操作又会重新进行evaluate,这个违背了我们的想法。

Partitioning的好处

许多Spark的操作都会通过网络按照key随机打乱数据。所有会被shuffling data的操作都可以从partitioning中获得好处,例如这些操作(cogroup(), groupWith(), join(), leftOuterJoin(), rightOuterJoin(), groupByKey(), reduceByKey(), combineByKey() and lookup())。

对于 reduceByKey()来说,在一个pre-partitioning的RDD上操作会把计算操作在本地的机器上运行,然后把结果从每个worker node发送到master。
对于join()或者cogroup()来说,在一个pre-partitioning的RDD上操作会使至少一个RDD不需要被打乱(not be shuffled)。

影响partitioning的操作

如果你调用一个map()方法在一个hash-partitioned RDD上面,因为这个map方法会改变元素的key,所以它是结果就没有partitioner。Spark不能检查你的方法来判断你的键是否有改变,但是它提供了2个方法供你使用mapValues()和flatMapValues(),这2个方法都只会对值进行修改而不会对key进行修改。

大概而言,这些操作的结果也是一个partitioner:
cogroup(), groupWith(), join(), leftOuterJoin(), rightOuterJoin(), groupByKey(), reduceByKey(), combineByKey(), partitionBy(), sort(), mapValues()
(if the parent RDD has a partitioner), flatMapValues() (if parent has a partitioner), and filter() (if parent has a partitioner)。

PageRank示例

PageRank算法是基于有多少东西与它有关联从而来判断一个实物的重要性等级。PageRank算法会用到许多的joins,所以它是一个使用RDD partitioning的好示例。

Python custom partitioner

如果在做PageRank的时候,www.cnn.com/news 与 www.cnn.com/sports 其实都是来自于同一个domain的,证明他们2个之间肯定有非常多的互相链接,所以我们想把他们group到相同的partition,这样就需要使用自定义的partitioner。

使用自定义的Partitioner非常简单,只需要传一个hash函数给partitionBy()方法。

import urlparse
def hash_domain(url):
    return hash(urlparse.urlparse(url).netloc)

rdd.partitionBy(20, hash_domain) # Create 20 partitions

注意:你传递的hash方法会被作为唯一标识符来和别的RDD比较,如果你想要对多个RDDs使用相同的partitioner,那就传递一个相同的方法而不要给每个RDD创建新的lambda函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值