Spark中ID发号器实现思路

欢迎转载,转载请注明出处:

Spark作为一个分布式处理框架,处理数据非常快。可是我也不知道作者基于什么样的设计哲学,限制了用户对于一些数据的操作。例如你无法改变一个RDD的内容。无法一个split把数据集分割成两份。无法获得RDD的分区信息,无法再map的时候知道自己处于哪个分区。这对于ID分配来说实在是太难办了。甚至想要一个1对1的笛卡尔积都没有。(如果有的话,我们可以计算待分配id数据的个数,然后生成一个数据个数长度的id列表,然后分配给他们即可)

经过我好几天的调查,尝试了很多方法。

最容易想到的方法将数据toLocalIterator,然后再给每条分配id。再join回去,这个方法很慢。而且会把数据放在一个节点上。代价太高。

网上给出的方法也主要是使用其他服务,例如zookeeper来做一个全局请求的发号器。

经过个人研究,实现了两套发号器思路。

1.最快速,最简单的方法适用于id不要求连续的情况,并且数据数目很少的情况。此时可以使用一个hash算法,根据数据的key来计算它的id。数据量少hash碰撞概率很小。毕竟id最主要的目的还是标识数据。这里推荐使用murmurhash。scala代码如下,这里charsets根据自己实际上求hash的key的字符集而定我这里是给URL分配的,因此ASC2的字符集就够(字符集越小,hash碰撞概率也越小):

 

import com.google.common.base.Charsets
import com.google.common.hash.Hashing
Hashing.murmur3_128().hashString(str, Charsets.US_ASCII).asLong.abs

2.是今天刚想到的一个方法,思路是这样:

利用spark的glom(),将rdd的每个分区转化成Array。这样就可以统计每个分区的元素个数。然后利用个数计算每个分区的数据ID起始下标,将其存到一个Map中,然后广播。话不多说上代码:

假设你的rdd存的是一行行数据,你此次分配的起始id为beginId是1。有两种思路获取带id的part第一种注释掉了,因为这种只能处理数据不同的情况,并且需要两步操作也会多一些。

 

 
val beginId = 1
val newMaxId = beginId + allNoIdData.count()//获取此次分配的最大id备用
//val allUrlPartArrayOld = allNoIdUrl.glom().map(e => 
// (if (e.length > 0) e.head else "empty", e)
//).persist(StorageLevel.MEMORY_AND_DISK_SER_2)
val allUrlPartArray = allNoIdData.mapPartitionsWithIndex((partId,iter)=>{
  Iterator((partId,iter))//变成了partId+这个part的数据。
}).persist(StorageLevel.MEMORY_AND_DISK_SER_2)
val keyNumMap = allUrlPartArray.map(e => (e._1, e._2.length)).collectAsMap()
val keyBeginIdMap = mutable.HashMap[Int, Long]()
keyNumMap.foreach(e => {
  keyBeginIdMap.put(e._1, beginId)
  beginId += e._2
})
val keyBeginIdMapBroadCast = sc.broadcast(keyBeginIdMap)
val dataIdRdd = allUrlPartArray.flatMap(e => {
  var currentId: Long = keyBeginIdMapBroadCast.value.getOrElse(e._1, newMaxId)
  e._2.map(f => {
    val result = (f, currentId)
    currentId += 1l
    result
  })
})

 

如果有感兴趣的可以相互交流。

2018-11-14后记:

再一次表示自己孤陋寡闻了,zipWithIndex()专门做这个事的。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值