我已经使用Kafka将数据按密钥排序到我的Spark Streaming分区中,即在任何其他节点上找不到在一个节点上找到的密钥。
我想使用redis及其incrby
(递增)命令作为状态引擎,并减少发送到redis的请求数量,我想通过对字数进行部分减少我的数据每个工作节点本身。 (键是标记+时间戳,以从字数获取我的功能)。
我想避免改组,让redis负责在工作节点之间添加数据。
即使我已经检查过数据在工作节点之间干净地分开,.reduce(_ + _)
(Scala语法)需要很长时间(几秒钟而不是地图任务的亚秒级),因为HashPartitioner似乎在洗牌我的将数据添加到随机节点以将其添加到那里。
如何在不使用Spark Streaming触发Scala中的混洗步骤的情况下,在每个分区上编写简单的字数减少?
注意DStream对象缺少一些RDD方法,只能通过transform
方法使用。
似乎我可以使用combineByKey
。我想跳过mergeCombiners()
步骤,而是将累积的元组留在原处。
这本书" Learning Spark"神秘地说:
如果我们知道我们的数据不会从中受益,我们可以在combineByKey()中禁用地图侧聚合。例如,groupByKey()禁用映射端聚合,因为聚合函数(附加到列表)不会保存任何空间。如果我们想要禁用map-side combine,我们需要指定分区器;现在你可以通过传递rdd.partitioner来使用源RDD上的分区器。
https://www.safaribooksonline.com/library/view/learning-spark/9781449359034/ch04.html
这本书继续没有提供如何做到这一点的语法,到目前为止我也没有任何运气。
更糟糕的是,据我所知,在Spark Streaming中没有为DStream RDD设置分区器,所以我不知道如何提供一个分区器来组合不会最终改组数据的KeyKey
另外," map-side"实际意味着mapSideCombine = false
具有什么后果呢?
combineByKey
的scala实现可在以下位置找到:
https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/PairRDDFunctions.scala
寻找combineByKeyWithClassTag
。
如果解决方案涉及自定义分区程序,请另外提供一个代码示例,了解如何将该分区程序应用于传入的DStream。
答案 0 :(得分:3)
这可以使用mapPartitions
来完成,它使用一个函数将一个分区上的输入RDD的迭代器映射到输出RDD上的迭代器。
为了实现字数统计,我映射到_._2
以删除Kafka密钥,然后使用foldLeft
执行快速迭代器字数统计,初始化mutable.hashMap
,然后转换为迭代器,用于形成输出RDD。
val myDstream = messages
.mapPartitions( it =>
it.map(_._2)
.foldLeft(new mutable.HashMap[String, Int])(
(count, key) => count += (key -> (count.getOrElse(key, 0) + 1))
).toIterator
)