我有一个Spark Streaming应用程序,该应用程序从Kafka中读取记录中的Hive表名,例如table1 .. table2 .. table3 .. etc。
我想在配置单元表上执行结构化查询,并将结果流式传输到另一个Kafka主题。
我有这样的
val hqls = rdd
.filter(record => record.value() != null && record.value().trim.length > 0)
.foreach(tableName=> publishData(tableName, sparkSession, kafkaProducer))
我的publishData
如下
val df = sparkSession.sql("select * from " + tableName)
df.foreach { row =>
// code to write to kafka
}
执行此操作后,在NullPointerException
调用中会收到一个sparkSession.sql
,如下所示:
org.apache.spark.SparkException: Job aborted due to stage failure: Task 13 in stage 12.0 failed 8 times, most recent failure: Lost task 13.7 in stage 12.0 (TID 838, cilhdwks0001.sys.cigna.com, executor 1): java.lang.NullPointerException
at org.apache.spark.sql.SparkSession.sessionState$lzycompute(SparkSession.scala:142)
at org.apache.spark.sql.SparkSession.sessionState(SparkSession.scala:140)
我在其他帖子中发现,无法在val df = sparkSession.sql(hql)
内运行rdd.foreach
,但是还没有找到正确的方法。
如果我按照以下方式将代码更改为使用collect
,它将起作用。为什么?
val tablenames = rdd
.filter(tableName => tableName != null && tableName.trim.length > 0)
.collect()
tablenames.foreach { tablename =>
publishData(tablename, sparkSession, kafkaProducer)
}
效率高吗?它将负载正确地分配给集群吗?
答案 0 :(得分:0)
我有一组配置单元名称,这些名称通过kafka到达我的Spark Streaming应用程序。我需要对每个表执行查询
一旦您开始将Hive表流式传输到Kafka,所有Spark看到的(无论是执行Spark SQL还是Spark Streaming或Spark Structured Streaming)都是记录(全部在一起)(可能是单个批处理),或者可能不对应于Hive表。一个处理周期只能看到一张桌子的一半或一张桌子的一半。这是不可预测的。
恕我直言,您必须发送一个标记记录,以便Spark可以将属于单个Hive表的所有记录过滤到数据集。那可能行得通,但我怀疑这是最理想的解决方案。
简而言之,在Spark中,您将处理单个Kafka记录(来自Hive表),除非发送一些额外的元数据,否则Spark不会知道整个Hive表是否在数据集中。
.foreach(tableName=> publishData
在publishData
中执行的任何操作都会在Spark执行器上发生,其中SparkContext
和SparkSession
不可用。您根本无法在执行程序上使用它们(按Spark设计),因此“在sparkSession.sql上出现NullPointerException” 这是预期的。
如果我将其更改为(...)
.collect()
(...),它将起作用 效率高,是否可以将负载正确地分配给集群?
Spark应用程序中的任何collect
都打破了Spark在群集中的各个节点之间分布大型数据集的前提,因此您可以处理多于一台机器可以处理的事情。任何collect
都会将所有分布式数据带到驱动程序的单个JVM中,并可能导致OutOfMemoryErrors(这可能就是您首先考虑使用Apache Spark处理大型数据集的原因)。