Spark结构化流中的多个聚合

时间:2016-12-07 06:42:18

标签: apache-spark apache-spark-sql spark-structured-streaming

我想在Spark Structured Streaming中进行多次聚合。

这样的事情:

  • 读取输入文件流(来自文件夹)
  • 执行聚合1(带有一些转换)
  • 执行聚合2(以及更多转换)

当我在Structured Streaming中运行它时,它会给我一个错误"流数据框架/数据集"不支持多个流聚合。

有没有办法在结构化流媒体中进行这样的多重聚合?

7 个答案:

答案 0 :(得分:9)

这不受支持,但还有其他方法。就像执行单个聚合并将其保存到kafka一样。从kafka读取并再次应用聚合。这对我有用。

答案 1 :(得分:1)

您未提供任何代码,因此我将使用引用here的示例代码。

让我们假设下面是DF使用的初始代码。

import pyspark.sql.functions as F
spark = SparkSession. ...

# Read text from socket
socketDF = spark \
    .readStream \
    .format("socket") \
    .option("host", "localhost") \
    .option("port", 9999) \
    .load()

socketDF.isStreaming()    # Returns True for DataFrames that have streaming sources

socketDF.printSchema()

# Read all the csv files written atomically in a directory
userSchema = StructType().add("name", "string").add("age", "integer")
csvDF = spark \
    .readStream \
    .option("sep", ";") \
    .schema(userSchema) \
    .csv("/path/to/directory")  # Equivalent to format("csv").load("/path/to/directory")

名称分组df并应用汇总功能 count sum balance

grouped = csvDF.groupBy("name").agg(F.count("name"), F.sum("age"), F.avg("age"))

答案 2 :(得分:0)

Spark 2.0不支持此功能,因为Structured Streaming API仍处于试验阶段。请参阅here以查看所有当前限制的列表。

答案 3 :(得分:0)

对于spark 2.2及更高版本(不确定早期版本),如果您可以将聚合设计为在 append 模式下使用 flatMapGroupWithState ,则可以进行与你要。 这里提到了限制Spark structured streaming - Output mode

答案 4 :(得分:0)

从Spark 2.4开始,不支持Spark结构化流中的多个聚合。支持这一点尤其棘手。事件时间处于“更新”模式,因为合计输出可能会随事件的发生而改变。在“追加”模式下支持此功能非常简单,但是spark目前还不支持真正的水印。

这里有一个提案以“附加”模式添加-https://github.com/apache/spark/pull/23576

如果有兴趣,您可以观看PR,并在此处张贴投票。

答案 5 :(得分:0)

从Spark结构化流2.4.5开始,无状态处理中不支持多种聚合。 但是,如果需要状态处理,则可以多次聚合。

在附加模式下,您可以对分组数据集(通过使用flatMapGroupWithState API获得)上的groupByKey API多次使用。

答案 6 :(得分:0)

TLDR-不支持此功能;在某些情况下,可能有解决方法。

长版-

  1. (黑客)

在某些情况下,可以使用解决方法,例如,如果您希望在低基数列的流查询中包含多个count(distinct),那么approx_count_distinct实际返回<通过将rsd自变量放到足够低的位置,可以精确计算出不同元素的数量(这是rox_count_distinct的第二个可选参数,默认为0.05。)

这里如何定义“低基数”?对于具有超过1000个唯一值的列,建议不要使用这种方法。

因此,在流式查询中,您可以执行以下操作-

(spark.readStream....
      .groupBy("site_id")
      .agg(approx_count_distinct("domain", 0.001).alias("distinct_domains")
         , approx_count_distinct("country", 0.001).alias("distinct_countries")
         , approx_count_distinct("language", 0.001).alias("distinct_languages")
      )
  )

这里证明它确实有效:

enter image description here

请注意,count(distinct)和count_approx_distinct给出相同的结果! 这是有关rsd参数count_approx_distinct的一些指导:

  • 对于具有100个不同值rsd 0.02 的列,是必需的;
  • 对于具有1000个不同值rsd 0.001 的列,是必需的。

PS。另请注意,由于我没有足够的耐心来完成该实验,因此不得不在具有10k个不同值的列上注释掉该实验。这就是为什么我提到您不应该对具有超过1k个不同值的列使用此技巧。为了使approx_count_distinct匹配超过1k个不同值的精确计数(差异),对于HyperLogLogPlusPlus algorithm的设计目标而言,rsd的方式可能太低了(该算法落后于roximate_count_distinct实现)。

  1. (很好,但涉及更多的方式)

正如其他人所提到的,您可以使用Spark的arbitrary stateful streaming来实现自己的聚合;并使用[flat]MapWithGroupState在单个流上根据需要进行尽可能多的聚合。与上述仅在某些情况下有效的黑客不同,这是一种合法且受支持的方法。此方法仅适用于Spark Scala API,不适用于PySpark。

  1. (也许这将是一个长期的解决方案)

一种正确的方法是在Spark Streaming中显示对本机多重聚合的支持-https://github.com/apache/spark/pull/23576-对此SPARK jira / PR投赞成票,如果对此感兴趣,请显示您的支持。

相关问题