我考虑使用Apache Spark流进行一些实时工作,但我不确定如何缓存数据集以用于连接/查找。
主要输入是来自Kafka的包含Id的json记录,我想使用查找数据集将该id转换为名称。查找数据集驻留在Mongo Db中,但我希望能够将其缓存在spark进程中,因为数据集很少变化(每隔几小时一次),所以我不想为每个输入记录点击mongo或重新加载每个火花批次中的所有记录,但我需要能够定期更新火花中保存的数据(例如每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
答案 1 :(得分:0)
这是一个相当简单的工作流程:
对于每批数据: