我有一个流式查询(Spark Structured Streaming),它接收来自Kafka主题(两个分区)的数据,如下所示:
val df = spark
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "172.29.57.25:9092,172.29.57.30:9092")
.option("subscribe", "mytopic")
.load()
.select(from_json(col("value").cast("string"), schema).as("record")).select("record.*")
我希望按日期/小时执行简单的聚合和分区,并在HDFS中保存到Parquet文件,如下所示:
val aggregationQuery = df.withColumn("ROP", from_unixtime((col("attributes.START_TIME")/1000), "yyyy-MM-dd HH:mm").cast(TimestampType))
.withColumn("date", to_date(col("ROP")))
.withColumn("hour", hour(col("ROP")))
.withColumn("timestamp", current_timestamp())
.withWatermark("timestamp", "0 minutes")
.groupBy(window(col("timestamp"), "10 seconds"), col("date"), col("hour"))
.agg(count("attributes.RECORDID").as("NumRecords"))
.coalesce(2)
输出到实木复合地板:
aggregationQuery.writeStream
.format("parquet")
.trigger(Trigger.ProcessingTime("10 seconds"))
.partitionBy("date", "hour")
.option("path", "hdfs://cloudera-cluster:8020/user/spark/proyecto1")
.option("checkpointLocation", "hdfs://cloudera-cluster:8020/user/spark/checkpointfolder")
.outputMode("append")
.start()
结果,我得到的文件夹结构类似于这个例子:
user/spark/proyecto1/date=2015-08-18/hour=20
在每个文件夹中,我在流式传输过程中每个Trigger获得2个Parquet文件。
我想了解'合并'和' partitionBy'操作正在处理我的数据,以及与此特定组合相关的任何风险。
顺便说一句,我的群集中只有2个节点。
答案 0 :(得分:0)
coalesce
将完整管道的并行度降低到2.由于它没有引入分析障碍,因此会向后传播,因此在实践中最好用repartition
替换它。partitionBy
创建您看到的目录结构,并在路径中编码值。它从叶子文件中删除相应的列。由于日期和时间基数较低,因此在这种情况下没有特别的风险。结合这两个创建观察目录结构并将每个叶子目录中的多个文件限制为最多两个。
答案 1 :(得分:0)
Coalesce:它减少了分区的数量。在这种情况下,如果n是默认的分区数,则将其减少为2.它将每个节点中的所有分区盲目地组合成一个分区,从而产生2.This可能是你在文件夹中获得2个文件的原因。
当你使用分区时,它会根据你在列中的值创建n个分区。就像每个唯一的密钥进入一个相应的分区一样。如果没有正确使用你可能最终会有大量的分区会在两个节点集群中产生开销