Apache spark 是否可以在Spark结构化流媒体中使用foreachBatch将两个不相交的数据集写入数据同步?
我正在尝试将数据从单个源写入多个数据链路(Mongo和Postgres DBs)。 传入数据Apache spark 是否可以在Spark结构化流媒体中使用foreachBatch将两个不相交的数据集写入数据同步?,apache-spark,apache-spark-sql,spark-structured-streaming,mongodb-kafka-connector,Apache Spark,Apache Spark Sql,Spark Structured Streaming,Mongodb Kafka Connector,我正在尝试将数据从单个源写入多个数据链路(Mongo和Postgres DBs)。 传入数据 Dataset<Row> df = spark .readStream() .format("kafka") .option("kafka.bootstrap.servers", "localhost:9092") .option("subscribe&quo
Dataset<Row> df = spark
.readStream()
.format("kafka")
.option("kafka.bootstrap.servers", "localhost:9092")
.option("subscribe", "topic1")
.load();
Dataset<Row> personalDetails = df.selectExpr("name", "id", "age");
personalDetails.writeStream()
.outputMode(OutputMode.Update())
.foreachBatch((dataframe, bachId) -> {
dataframe.write().format("com.mongodb.spark.sql.DefaultSource").mode(SaveMode.Append)
.option("uri", "mongodb://localhost/employee")
.option("database", "employee")
.option("collection", "PI").save();
}).start();
Dataset<Row> salDetails = df.selectExpr("basicSal", "bonus");
salDetails.writeStream()
.outputMode(OutputMode.Update())
.foreachBatch((dataframe, bachId) -> {
dataframe.write().format("com.mongodb.spark.sql.DefaultSource").mode(SaveMode.Append)
.option("uri", "mongodb://localhost/employee")
.option("database", "employee")
.option("collection", "SAL").save();
}).start();
Dataset df=spark
.readStream()
.格式(“卡夫卡”)
.option(“kafka.bootstrap.servers”,“localhost:9092”)
.期权(“认购”、“主题1”)
.load();
Dataset personalDetails=df.selectExpr(“姓名”、“身份证”、“年龄”);
personalDetails.writeStream()
.outputMode(outputMode.Update())
.foreachBatch((dataframe,bachId)->{
dataframe.write().format(“com.mongodb.spark.sql.DefaultSource”).mode(SaveMode.Append)
.选项(“uri”mongodb://localhost/employee")
.选项(“数据库”、“员工”)
.option(“collection”、“PI”).save();
}).start();
数据集salDetails=df.selectExpr(“基本”、“奖金”);
salDetails.writeStream()
.outputMode(outputMode.Update())
.foreachBatch((数据帧,bachId)->{
dataframe.write().format(“com.mongodb.spark.sql.DefaultSource”).mode(SaveMode.Append)
.选项(“uri”mongodb://localhost/employee")
.选项(“数据库”、“员工”)
.option(“collection”、“SAL”).save();
}).start();
问题是,我可以看到火花正在打开两个流,并读取相同的事件两次。
是否可以读取一次并应用不同的转换和写入不同的集合?您应该缓存数据帧。 见: 写入多个位置-如果要将流式查询的输出写入多个位置,则只需多次写入输出数据帧/数据集即可。但是,每次尝试写入都可能导致重新计算输出数据(包括可能重新读取输入数据)。为了避免重新计算,应该缓存输出数据帧/数据集,将其写入多个位置,然后取消缓存 以及他们的例子:
streamingDF.writeStream.foreachBatch { (batchDF: DataFrame, batchId: Long) =>
batchDF.persist()
batchDF.write.format(...).save(...) // location 1
batchDF.write.format(...).save(...) // location 2
batchDF.unpersist()
}
您可以将所有代码放在一个foreachBatch
中,并将数据帧写入两个接收器。您可以通过缓存数据帧,并在此缓存的数据帧上执行selectExpr
,然后保存它来实现这一点
作为旁注-请注意,在任何情况下,如果您想要“全部或全无”(即,您不希望出现您写信给mongo而不是postgres的情况),您必须只使用一个
foreachBatch
,否则(如果您有2个foreachBatch
,如您的问题所示)您有两个独立的批处理—对于相同的数据,当另一个成功时,其中一个批处理可能会失败。最后,我可以使用Spark 3.1.1使用结构化流表API解决此问题
自Spark 3.1以来,您还可以使用DataStreamReader.table()将表读取为流式数据帧,并使用DataStreamWriter.toTable()将流式数据帧写入为表:
//流到myTable
df.writeStream()
.选项(“检查点位置”,“/tmp/test”)
.toTable(“myTable”);
//来自myTable的流
数据集tableDf=spark.readStream()
.表格(“myTable”);
参考:
有了这个,我可以解决从源代码“kafka”多次读取的问题。这样,它将只创建一个Kafka使用者,并将数据流传输到表中。从那个里,它将使用多个流从表中读取数据,并在表数据集的顶部应用额外的转换。
因此,完整的示例如下所示:
Dataset<Row> df = spark
.readStream()
.format("kafka")
.option("kafka.bootstrap.servers", "localhost:9092")
.option("subscribe", "topic1")
.load();
// Stream to myTable
df.writeStream.option("checkpointLocation", "c:/temp/test").toTable("myTable");
// Stream from myTable
Dataset<Row> tableDf = spark.readStream().table("myTable");
Dataset<Row> personalDetails = tableDf.selectExpr("name", "id", "age");
personalDetails.writeStream()
.outputMode(OutputMode.Update())
.foreachBatch((dataframe, bachId) -> {
dataframe.write().format("com.mongodb.spark.sql.DefaultSource").mode(SaveMode.Append)
.option("uri", "mongodb://localhost/employee")
.option("database", "employee")
.option("collection", "PI").save();
}).start();
Dataset<Row> salDetails = tableDf.selectExpr("basicSal", "bonus");
salDetails.writeStream()
.outputMode(OutputMode.Update())
.foreachBatch((dataframe, bachId) -> {
dataframe.write().format("com.mongodb.spark.sql.DefaultSource").mode(SaveMode.Append)
.option("uri", "mongodb://localhost/employee")
.option("database", "employee")
.option("collection", "SAL").save();
}).start();
Dataset df=spark
.readStream()
.格式(“卡夫卡”)
.option(“kafka.bootstrap.servers”,“localhost:9092”)
.期权(“认购”、“主题1”)
.load();
//流到myTable
df.writeStream.option(“检查点位置”,“c:/temp/test”).toTable(“myTable”);
//来自myTable的流
数据集tableDf=spark.readStream().table(“myTable”);
Dataset personalDetails=tableDf.selectExpr(“姓名”、“身份证”、“年龄”);
personalDetails.writeStream()
.outputMode(outputMode.Update())
.foreachBatch((数据帧,bachId)->{
dataframe.write().format(“com.mongodb.spark.sql.DefaultSource”).mode(SaveMode.Append)
.选项(“uri”mongodb://localhost/employee")
.option(“数据库”、“员工”)
.option(“collection”、“PI”).save();
}).start();
数据集salDetails=tableDf.selectExpr(“基本”、“奖金”);
salDetails.writeStream()
.outputMode(outputMode.Update())
.foreachBatch((数据帧,bachId)->{
dataframe.write().format(“com.mongodb.spark.sql.DefaultSource”).mode(SaveMode.Append)
.选项(“uri”mongodb://localhost/employee")
.选项(“数据库”、“员工”)
.option(“collection”、“SAL”).save();
}).start();
foreachBatch在您只想在两个位置同步之间写入流而无需额外处理的情况下运行良好。foreachBatch
在您只想在两个位置同步之间写入流而无需额外处理的情况下运行良好。查看注释“应用其他数据帧操作-流式数据帧中不支持许多数据帧和数据集操作”,我不确定您为什么会这样说-您是否尝试过我建议的解决方案?foreachBatch确实允许您在batchDf上执行操作,例如,通过在batchDf上运行sql
来创建新的数据帧。另外,请阅读链接中的剩余句子-“使用foreachBatch,您可以在每个微批输出上应用其中一些操作”.我已经在使用foreachBatch将相同的数据写入多个数据接收器。如问题中所述,我需要将两个不同的(不相交的)数据集写入数据接收器。在foreachBatch内部,不可能应用在Dataset上定义的所有转换
Dataset<Row> df = spark
.readStream()
.format("kafka")
.option("kafka.bootstrap.servers", "localhost:9092")
.option("subscribe", "topic1")
.load();
// Stream to myTable
df.writeStream.option("checkpointLocation", "c:/temp/test").toTable("myTable");
// Stream from myTable
Dataset<Row> tableDf = spark.readStream().table("myTable");
Dataset<Row> personalDetails = tableDf.selectExpr("name", "id", "age");
personalDetails.writeStream()
.outputMode(OutputMode.Update())
.foreachBatch((dataframe, bachId) -> {
dataframe.write().format("com.mongodb.spark.sql.DefaultSource").mode(SaveMode.Append)
.option("uri", "mongodb://localhost/employee")
.option("database", "employee")
.option("collection", "PI").save();
}).start();
Dataset<Row> salDetails = tableDf.selectExpr("basicSal", "bonus");
salDetails.writeStream()
.outputMode(OutputMode.Update())
.foreachBatch((dataframe, bachId) -> {
dataframe.write().format("com.mongodb.spark.sql.DefaultSource").mode(SaveMode.Append)
.option("uri", "mongodb://localhost/employee")
.option("database", "employee")
.option("collection", "SAL").save();
}).start();