Apache spark streaming - 用于加入的缓存数据集

时间:2014-07-01 07:30:12

标签: streaming apache-spark

我考虑使用Apache Spark流进行一些实时工作,但我不确定如何缓存数据集以用于连接/查找。

主要输入是来自Kafka的包含Id的json记录,我想使用查找数据集将该id转换为名称。查找数据集驻留在Mongo Db中,但我希望能够将其缓存在spark进程中,因为数据集很少变化(每隔几小时一次),所以我不想为每个输入记录点击mongo或重新加载每个火花批次中的所有记录,但我需要能够定期更新火花中保存的数据(例如每2小时)。

这样做的最佳方式是什么?

感谢。

2 个答案:

答案 0 :(得分:2)

我自己一直在想这个问题。特别是我想知道是否可以在Spark中实际实现数据库数据库。

答案是肯定的。首先,您需要一个首先将主数据集缓存到内存中的程序,然后每隔几个小时就会执行一个优化的join-with-tiny来更新主数据集。现在很明显Spark会有一个方法可以实现一个小的连接(也许它已经在1.0.0中了 - 我的堆栈一直停留在0.9.0,直到CDH 5.1.0出来)。

无论如何,您可以通过获取周期性的双时数据集并将其转换为HashMap然后将其作为广播变量广播来手动实现微小连接。这意味着HashMap将被复制,但每个节点只复制一次(将其与仅引用Map进行比较 - 每个任务复制一次 - 成本要高得多)。然后,您将获取主数据集并使用广播的地图添加新记录。然后你可以定期(每晚)保存到hdfs或其他东西。

所以这里有一些邋pseudo的伪代码来阐明:

var mainDataSet: RDD[KeyType, DataType] = sc.textFile("/path/to/main/dataset")
  .map(parseJsonAndGetTheKey).cache()

everyTwoHoursDo {
  val newData: Map[KeyType, DataType] = sc.textFile("/path/to/last/two/hours")
    .map(parseJsonAndGetTheKey).toarray().toMap

  broadcast(newData)

  val mainDataSetNew = 
    mainDataSet.map((key, oldValue) => (key, 
      newData.get(key).map(newDataValue => 
        update(oldValue, newDataValue))
      .getOrElse(oldValue)))
    .cache()

  mainDataSetNew.someAction() // to force execution

  mainDataSet.unpersist()
  mainDataSet = mainDataSetNew
}

我还认为你可以非常聪明并使用自己的自定义索引自定义分区,然后使用自定义方式更新分区,以便每个分区本身都有一个子图。然后,您可以跳过更新您知道不会包含newData中出现的任何键的分区,并优化更新过程。

我个人认为这是一个非常酷的想法,而且好处是您的数据集已经准备好用于某些分析/机器学习。倒是你有点重新发明轮子。看看使用Cassandra可能是一个更好的主意,因为Datastax与Databricks(制作Spark的人)合作,最终可能会支持某种开箱即用的东西。

进一步阅读:

http://spark.apache.org/docs/latest/programming-guide.html#broadcast-variables

http://www.datastax.com/2014/06/datastax-unveils-dse-45-the-future-of-the-distributed-database-management-system

答案 1 :(得分:0)

这是一个相当简单的工作流程:

对于每批数据:

  1. 将批量JSON数据转换为DataFrame(b_df)。
  2. 将MongoDB中的查找数据集作为DataFrame(m_df)读取。然后缓存,m_df.cache()
  3. 使用b_df.join加入数据(m_df,“join_field”)
  4. 执行所需的聚合,然后写入数据源。