Apache spark Spark有状态结构化流:mapGroupsWithState中的状态变得太大

Apache spark Spark有状态结构化流:mapGroupsWithState中的状态变得太大,apache-spark,aggregate,spark-streaming,spark-structured-streaming,Apache Spark,Aggregate,Spark Streaming,Spark Structured Streaming,我正在尝试使用mapGroupsWithState方法对传入的数据流进行有状态结构化流处理。但我面临的问题是,我为groupByKey选择的键使我的状态太大太快。显而易见的解决方法是更改密钥,但我希望在更新方法中应用的业务逻辑要求密钥与我现在拥有的密钥完全相同,或者如果可能,访问所有密钥的组状态 例如,我有一个来自不同组织的数据流,通常一个组织包含userId、personId等。请参见下面的代码: val stream: Dataset[User] = dataFrame.as[User] v

我正在尝试使用mapGroupsWithState方法对传入的数据流进行有状态结构化流处理。但我面临的问题是,我为groupByKey选择的键使我的状态太大太快。显而易见的解决方法是更改密钥,但我希望在更新方法中应用的业务逻辑要求密钥与我现在拥有的密钥完全相同,或者如果可能,访问所有密钥的组状态

例如,我有一个来自不同组织的数据流,通常一个组织包含userId、personId等。请参见下面的代码:

val stream: Dataset[User] = dataFrame.as[User]
val noTimeout = GroupStateTimeout.NoTimeout
val statisticStream = stream
    .groupByKey(key => key.orgId)
    .mapGroupsWithState(noTimeout)(updateUserStatistic)

val df = statisticStream.toDF()

val query = df
    .writeStream
    .outputMode(Update())
    .option("checkpointLocation", s"$checkpointLocation/$name")
    .foreach(new UserCountWriter(spark.sparkContext.getConf))
    .outputMode(Update())
    .queryName(name)
    .trigger(Trigger.ProcessingTime(Duration.apply("10 seconds")))
个案类别:

case class User(
  orgId: Long,
  profileId: Long,
  userId: Long)

case class UserStatistic(
  orgId: Long,
  known: Long,
  uknown: Long,
  userSeq: Seq[User])
更新方法:

def updateUserStatistic(
  orgId: Long, 
  newEvents: Iterator[User], 
  oldState: GroupState[UserStatistic]): UserStatistic = {
    var state: UserStatistic = if (oldState.exists) oldState.get else UserStatistic(orgId, 0L, 0L, Seq.empty)
    for (event <- newEvents) {
    //business logic like checking if userId in this organization is of certain type and then accordingly update the known or unknown attribute for that particular user.  
    oldState.update(state)
    state
  }
def updateUserStatistic(
奥吉德:很长,
newEvents:迭代器[用户],
oldState:GroupState[UserStatistic]:UserStatistic={
变量状态:UserStatistic=if(oldState.exists)oldState.get else UserStatistic(orgId,0L,0L,Seq.empty)

对于(event您的状态大小一直在增加,因为在当前实现中,不会从GroupState中删除任何键/状态对

为了准确缓解您面临的问题(无限增长状态),
mapGroupsWithState
方法允许您使用超时。您可以在两种类型的超时之间进行选择:

  • 使用
    GroupStateTimeout.processingtimeout
    GroupState.setTimeoutDuration()
    处理超时,或
  • 使用
    GroupStateTimeout.eventtimetimetimetimeout
    GroupState.setTimeoutTimestamp()
    进行事件超时
请注意,它们之间的区别是基于持续时间的超时和更灵活的基于时间的超时

在trait
GroupState
的ScalaDocs中,您将找到一个关于如何在映射函数中使用超时的好模板:

def映射函数(键:字符串,值:迭代器[Int],状态:GroupState[Int]):字符串={
if(state.hastinmedout){//如果在超时时调用,请删除该状态
state.remove()
}否则,如果(state.exists){//如果state存在,则将其用于处理
val existingState=state.get//获取现有状态
val shouldRemove=…//决定是否删除状态
如果(应移除){
state.remove()//删除状态
}否则{
val newState=。。。
state.update(newState)//设置新状态
state.setTimeoutDuration(“1小时”)//设置超时
}
}否则{
val initialState=。。。
state.update(initialState)//设置初始状态
state.setTimeoutDuration(“1小时”)//设置超时
}
...
//归还某物
}

处理单个批处理时,状态是否会在一段时间内增长过大?@vatsalmevada-在一段时间内。在上面的示例中,随着新用户ID不断进入组织,状态将水平增长。