Learning Spark 笔记(六) -- 指定分区信息改善join()等的操作

本文介绍了如何通过使用`partitionBy()`指定分区信息来优化Spark中的`join()`操作,以减少资源和时间消耗。当一个RDD数据量大而另一个小,join会导致全 Shuffle。通过预先分区并持久化大数据量的RDD,可以避免不必要的shuffle。另外,还可以通过广播小数据量的RDD并使用`map()`来进一步优化,避免join操作。

9 . 默认情况下,join()操作会对两个RDD的主键做哈希以分区,通过网络将主键相同的元素发送到同一台机器上,然后根据相同的主键再进行连接。例子如下:

val sc = new SparkContext()
val userData = sc.sequenceFile[UserID,LinkInfo]("hdfs://...").persist
def processNewLogs(logFileName:String){
    val events = sc.sequenceFile[UserID, LinkInfo](logFileName)
    //RDD of (UserID,(UserInfo,LinkInfo)) pairs
    val joined = usersData.join(events)
    val offTopicVisits = joined.filter {
        // Expand the tuple into its components
        case (userId, (userInfo, linkInfo)) => 
            !userInfo.topics.contains(linkInfo.topic)
    }.count()
    println("Number of visits to non-subscribed opics: " + offTopicVisits)
}

这样的弊端是,两个RDD都会进行shuffle,资源和时间的消耗比较严重。如下图所示:
这里写图片描述
如果userData是一个大数据量、events是一个小数据量的话,每一次join()操作就会进行一次shuffle,这样的弊端体现的会更明显。为解决这个问题,我们可以利用partionBy()来指定分区信息。代码修改如下:

...
val userData = sc.sequenceFile[UserID,LinkInfo]("hdfs://...")
//指定分区信息为100个哈希分区
.partionBy(new HashPartiotioner(100))
//持久化操作,没有持久化等于白做指定分区信息的操作
.persist
...

在之后的join()操作,由于userData含有分区信息,且joined会有和userData一样的分区信息,那么userData就不会有shuffle。比如此处的userData和joined都有HashPartitioner信息,那么userData和joined中的元素只要key相同,就一定会在同一台机器上,所以userData中的数据只会是在本地移动。还一个值得注意的地方是,partionBy()操作也是shuffle,所以在指定分区信息后必须要有持久化操作。如果没有持久化操作,每次执行都会有partitionBy()的shuffle操作,那么就等于啥也没做甚至平白无故又增添shuffle。变化如下图所示:
这里写图片描述

还有另一种改进的方法也可以避免大数据量RDD的shuffle,那就是广播小数据量RDD,弃用join()操作,使用map()操作完成相同功能的操作。

def processNewLogs(logFileName:String){
    val events = sc.sequenceFile[UserID, LinkInfo](logFileName)
    val small = sc.brocast(events)
    //RDD of (UserID,(UserInfo,LinkInfo)) pairs
    val joined = userData.map(x=>{
        val ss = small.value
        ss.map(y=>{
            var tmp:(String,(String,String)) = null
            if(x._1 == y)
                tmp = (x._1,(x._2,y._2))
            tmp
        })
    })
    val offTopicVisits = joined.filter {
        // Expand the tuple into its components
        case (userId, (userInfo, linkInfo)) => 
            !userInfo.topics.contains(linkInfo.topic)
    }.count()
    println("Number of visits to non-subscribed opics: " + offTopicVisits)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值