在我的应用程序中,我从Kafka队列获得了一个帐户流(使用带有kafka的Spark流)
我需要从S3获取与这些帐户相关的属性,因此我计划缓存S3结果数据帧,因为S3数据暂时不会更新至少一天,未来可能会很快变为1小时或10分钟。所以问题是我如何定期刷新缓存的数据帧而不停止进程。
**更新:我计划在S3中发生更新时将事件发布到kafka,使用SNS和AWS lambda,我的流应用程序将订阅该事件并根据此事件刷新缓存的数据帧(基本上是unpersist( )缓存并从S3重新加载) 这是一个好方法吗?
答案 0 :(得分:5)
据我所知,执行您所要求的唯一方法是在新数据到达时从S3重新加载DataFrame,这意味着您还必须重新创建流式DF并重新启动查询。这是因为DataFrames基本上是不可变的。
如果要在不重新加载DataFrame的情况下更新(mutate)数据,则需要尝试其中一个datastores that integrate with or connect to Spark并允许突变。我所知道的是SnappyData。
答案 1 :(得分:0)
实现的最简单方法,下面的代码读取每个批次的维度数据文件夹,但请记住,新的维度数据值(在我的情况下为国家/地区名称)必须是一个新文件。
package com.databroccoli.streaming.dimensionupateinstreaming
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, ForeachWriter, Row, SparkSession}
import org.apache.spark.sql.functions.{broadcast, expr}
import org.apache.spark.sql.types.{StringType, StructField, StructType, TimestampType}
object RefreshDimensionInStreaming {
def main(args: Array[String]) = {
@transient lazy val logger: Logger = Logger.getLogger(getClass.getName)
Logger.getLogger("akka").setLevel(Level.WARN)
Logger.getLogger("org").setLevel(Level.ERROR)
Logger.getLogger("com.amazonaws").setLevel(Level.ERROR)
Logger.getLogger("com.amazon.ws").setLevel(Level.ERROR)
Logger.getLogger("io.netty").setLevel(Level.ERROR)
val spark = SparkSession
.builder()
.master("local")
.getOrCreate()
val schemaUntyped1 = StructType(
Array(
StructField("id", StringType),
StructField("customrid", StringType),
StructField("customername", StringType),
StructField("countrycode", StringType),
StructField("timestamp_column_fin_1", TimestampType)
))
val schemaUntyped2 = StructType(
Array(
StructField("id", StringType),
StructField("countrycode", StringType),
StructField("countryname", StringType),
StructField("timestamp_column_fin_2", TimestampType)
))
val factDf1 = spark.readStream
.schema(schemaUntyped1)
.option("header", "true")
.csv("src/main/resources/broadcasttest/fact")
var countryDf: Option[DataFrame] = None: Option[DataFrame]
def updateDimensionDf() = {
val dimDf2 = spark.read
.schema(schemaUntyped2)
.option("header", "true")
.csv("src/main/resources/broadcasttest/dimension")
if (countryDf != None) {
countryDf.get.unpersist()
}
countryDf = Some(
dimDf2
.withColumnRenamed("id", "id_2")
.withColumnRenamed("countrycode", "countrycode_2"))
countryDf.get.show()
}
factDf1.writeStream
.outputMode("append")
.foreachBatch { (batchDF: DataFrame, batchId: Long) =>
batchDF.show(10)
updateDimensionDf()
batchDF
.join(
countryDf.get,
expr(
"""
countrycode_2 = countrycode
"""
),
"leftOuter"
)
.show
}
.start()
.awaitTermination()
}
}