数据处理中, 经常会遇到取TopN的问题. 在Spark中,取TopN有如下的方法:
生成rdd
读取数据源的数据并转为rdd.
val rdd = sc.textFile()
分区
将rdd划分分区,分区的个数根据实际的数据量和计算集群机器的数量以及核心数确定.
val partitionedRDD = rdd.coalesce(partitions)
kv变换
把每条数据转换为(k,v)形式, k为键,v为值且类型为数字类型.
val kv = partitionedRDD.map(line => .. (k,v))
去重
如果键值不唯一,则需要根据键值去重,并把key相同的value值进行累加.
val uniqueKeys = kv.reduceByKey((a,b) => a + b)
每个分区本地TopN
利用分布式计算的优势,在各个分区生成一个本地TopN.这里有几种方法可以选择.
方法1: mapParititions + SortedMap(TreeMap)
val partitions = uniqueKeys.mapPartitions(mp => {
val localTopN = new TreeMap[Integer, String]()
for (item <- mp){
localTopN.put(item._2, item._1)
// 只保留top N
if (localTopN.size > N){
// 删除频度最小的元素
localTopN.remove(localTopN.first)
}
}
Collections.singletonList(localTopN)
})
收集每个分区的TopN,取最终的TopN
val allTopN = partitions.collect()
val finalTopN = new TreeMap[Integer, String]()
for (item <- allTopN){
finalTopN.put(item._1, item._2)
// 只保留top N
if (finalTopN.size > N){
finalTopN.remove(finalTopN.first)
}
}
方法2:Spark提供了topN的算子, 使用takeOrdered()
val topNResult = uniqueKeys.takeOrdered(N)(implicit Ordering[T])