我正在使用Kafka话题。本主题有3个分区。 我使用foreachRDD处理每个批处理RDD(使用processData方法处理每个RDD,最终从中创建一个DataSet)。
现在,您可以看到我有计数变量,并且我在“processData”方法中递增此计数变量以检查我已经处理了多少实际记录。 (据我所知,每个RDD都是kafka主题记录的集合,数量取决于批处理间隔大小)
现在,输出是这样的:
1 1 1 2 3 2 4 3 5 ....
这让我认为它是因为我可能有3个消费者(因为我有3个分区),而且每个都会分别调用“foreachRDD”方法,所以同一个计数被打印不止一次,因为每个消费者可能已经缓存了它的计数副本。
但是我获得的最终输出DataSet具有所有记录。
那么,Spark内部联合所有数据吗?它是如何形成联合的? 我试图了解行为,以便我可以形成我的逻辑
int count = 0;
messages.foreachRDD(new VoidFunction<JavaRDD<ConsumerRecord<K, String>>>() {
public void call(JavaRDD<ConsumerRecord<K, V>> rdd) {
System.out.println("NUmber of elements in RDD : "+ rdd.count());
List<Row> rows = rdd.map(record -> processData(record))
.reduce((rows1, rows2) -> {
rows1.addAll(rows2);
return rows1;
});
StructType schema = DataTypes.createStructType(fields);
Dataset ds = ss.createDataFrame(rows, schema);
ds.createOrReplaceTempView("trades");
ds.show();
}
});
答案 0 :(得分:1)
这些假设并不完全准确。
foreachRDD
是Spark Streaming中所谓的output operations
之一。 output operations
的功能是按batch interval
指定的间隔安排提供的闭包。该闭包中的代码在spark驱动程序上每batch interval
执行一次。未在群集中分发。
特别是,foreachRDD
是一个通用output operation
,它提供对DStream中底层RDD的访问。应用于该RDD的操作将在Spark群集上执行。
因此,回到原始问题的代码,foreachRDD
闭包中的代码(例如System.out.println("NUmber of elements in RDD : "+ rdd.count());
)在驱动程序上执行。这也是我们可以在控制台中看到输出的原因。请注意,此rdd.count()
中的print
将触发群集上的count
RDD,因此count
是一个向驱动程序返回值的分布式操作,然后 - 在驱动程序上 - print
操作发生。
现在转变了RDD:
rdd.map(record -> processData(record))
正如我们所提到的,应用于RDD
的操作将在群集上执行。并且执行将在Spark执行模型之后执行;也就是说,转换被组合成阶段并应用于基础数据集的每个分区。鉴于我们正在处理3个kafka主题,我们将在Spark中有3个相应的分区。因此,processData
将对每个分区应用一次。
那么,Spark内部联合所有数据吗?它如何弄清楚要结合什么?
与Spark Streaming的输出操作相同,我们有针对Spark的操作。操作可能会对数据应用操作并将结果提供给驱动程序。最简单的操作是collect
,它将完整的数据集带给驱动程序,存在它可能不适合内存的风险。其他常见操作count
汇总了数据集中的记录数,并将单个数字返回给驱动程序。
在上面的代码中,我们使用reduce
,这也是一个应用提供的函数并将结果数据带到驱动程序的操作。这是在问题中表达的“内部联合所有数据”的行动的使用。在reduce表达式中,我们实际上收集了分发到单个本地集合中的所有数据。这相当于:rdd.map(record -> processData(record)).collect()
如果打算创建数据集,我们应该首先避免将所有数据“移动”到驱动程序。
更好的方法是:
val rows = rdd.map(record -> processData(record))
val df = ss.createDataFrame(rows, schema);
...
在这种情况下,所有分区的数据将保留在它们所在的执行程序的本地。
请注意,应避免将数据移至驱动程序。它很慢并且在大型数据集的情况下可能会使作业崩溃,因为驱动程序通常无法保存群集中的所有可用数据。