spark大数据分析:spark Struct Strreaming(22)基于Watermark处理延迟数据

文章目录

      • 作用
      • 基于update模式,实现wordCount,结合waterMark处理延迟数据
      • 基于Append模式 ,实现wordCount,结合waterMark处理延迟数据
      • 底层工作原理
      • Watermark机制与输出模式

作用

在数据分析系统中,Struct Strreaming可以持续按照事件时间聚合数据,在此过程中并不能保证数据按照事件时间大小依次达到,在某一个时刻接受到数据远远落后之前批次已经处理过的事件时间,发生这种情况时,需要结合业务需要对延迟数据进行过滤

默认情况下,无论数据延迟多久,数据根据事件时间生成若干静态的时间窗口,即使延迟,也能按照所属的时间窗口正确聚合,该情况下,数据完成聚合已经输出也不能重内存中移除,需要保留当前状态,随着数据不断流入,状态持续增长,造成程序不稳定
水印主要解决以下问题
(1) 处理聚合过程中的延迟数据
(2)减少内存中维护的聚合状态

基于update模式,实现wordCount,结合waterMark处理延迟数据

package struct

import java.text.SimpleDateFormat

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.execution.streaming.FileStreamSource.Timestamp
import org.apache.spark.sql.streaming.Trigger

object StructStream08 {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession
      .builder
      .master("local[*]")
      .appName("StructStream08")
      .getOrCreate()
    val lines = spark.readStream
      .format("socket")
      .option("host", "note01")
      .option("port", 9999)
      .load()
    val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    import org.apache.spark.sql.functions._
    import spark.implicits._
    val words = lines.as[String].map(s => {
      val arr = s.split(",")
      val date = sdf.parse(arr(0))
      (new Timestamp(date.getTime), arr(1))
    }).toDF("ts", "word")

    val wordCounts = words.withWatermark("ts", "2 minutes")
      .groupBy(
        window($"ts", "10 minutes", "2 minutes")
        , $"word"
      ).count()
    val query = wordCounts.writeStream
      .outputMode("update")
      .trigger(Trigger.ProcessingTime(0))
      .format("console")
      .start()

    query.awaitTermination()
  }
}

通过withWatermark方法设置Delay Threshold(延迟阈值)

ts:事件时间所在列名
2 minutes : Delay Threshold(延迟阈值)数据允许延迟的时长为2 min,waterMark 为已处理的最大事件时间允许延迟时间,程序会根据每一批数据重新计算waterMark,但是waterMark只会递增不会减少

基于Append模式 ,实现wordCount,结合waterMark处理延迟数据

...
  val query = wordCounts.writeStream
      .outputMode("append")
      .trigger(Trigger.ProcessingTime(0))
      .format("console")
      .start()
 ...

只输出新增数据,输出后数据无法变更

底层工作原理

将Threshold(延迟时长)转换为CalendarInterval实例,转入时间格式不正确抛出异常,如果解析完事负数,抛出异常

  def withWatermark(eventTime: String, delayThreshold: String): Dataset[T] = withTypedPlan {
    val parsedDelay =
      Option(CalendarInterval.fromString("interval " + delayThreshold))
        .getOrElse(throw new AnalysisException(s"Unable to parse time delay '$delayThreshold'"))
    require(parsedDelay.milliseconds >= 0 && parsedDelay.months >= 0,
      s"delay threshold ($delayThreshold) should not be negative.")
    EliminateEventTimeWatermark(
      EventTimeWatermark(UnresolvedAttribute(eventTime), parsedDelay, logicalPlan))
  }

通过正则匹配时间,然后定义查找最大事件时间,通过累加器查找分区中最大事件时间,实例化注册累加器并更新Watermark,在每一个当前批次中,使用上一个批次计算得出的Watermark过滤过期数据,并清理过期状态

Watermark机制与输出模式

(1)Watermark在用于基于时间的有状态聚合操作时,该时间可以基于窗口也可以基于事件时间本身
(2)输出模式为(Complete)时,必须存在聚合操作,每次都要输出之前所有聚合结果,使用Watermark无意义
(3)输出模式为Append,设置Watermark使用聚合操作,从另一个层面说,Watermark定义在Append模式中何时输出聚合结果,并清理过期状态
(4)输出模式Update.Watermark主要用于过滤过期数据,并及时清理过期状态
(5)Watermark会在处理当前批次数据时进行更新,在处理下一批次生效,若节点故障,延迟若干批次生效

你可能感兴趣的:(spark-鲨鱼)