如何在Spark Streaming的分区内“减少”,也许使用combineByKey?

时间:2016-09-29 04:42:19

标签: scala apache-spark redis spark-streaming partitioning

我已经使用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。

1 个答案:

答案 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
  )