Apache flink flink聚合状态巨大,如何修复

Apache flink flink聚合状态巨大,如何修复,apache-flink,flink-streaming,Apache Flink,Flink Streaming,我试图用不同的窗口大小来计算流中的数据。窗口的大小是在steam数据中,所以我使用自定义的WindowAssigner和AggregateFunction,但状态是从一小时到30天的巨大窗口范围 在我看来,聚合状态只是存储中间结果 有什么不对劲吗 公共类元素ProcessingTime扩展WindowAssigner{ @重写公共集合assignWindowsElement元素、长时间戳、WindowAssignerContext上下文{ 长滑动=时间。秒10。秒; long size=elem

我试图用不同的窗口大小来计算流中的数据。窗口的大小是在steam数据中,所以我使用自定义的WindowAssigner和AggregateFunction,但状态是从一小时到30天的巨大窗口范围

在我看来,聚合状态只是存储中间结果

有什么不对劲吗

公共类元素ProcessingTime扩展WindowAssigner{ @重写公共集合assignWindowsElement元素、长时间戳、WindowAssignerContext上下文{ 长滑动=时间。秒10。秒; long size=element.getTime*60*1000; timestamp=context.getCurrentProcessingTime; 列表窗口=新阵列限制大小/幻灯片; long lastStart=TimeWindow.GetWindowsStartWithOffsetTimeStamp,0,幻灯片; 对于长开始=最后开始;开始>时间戳-大小;开始-=幻灯片{ windows.addnew TimeWindowsStart,开始+大小; } 返回窗口; } @重写公共触发器getDefaultTriggerStreamExecutionEnvironment环境{ return ElementTimeTrigger.create; } @重写公共类型序列化程序GetWindowsSerializerExecutionConfig executionConfig{ 返回新的时间窗口。序列化程序; } @重写公共布尔值isEventTime{ 返回false; } } 公共类CountAggregate实现了AggregateFunction{ @重写公共聚合结果createAccumulator{ AggregateResult结果=新的AggregateResult; 结果:setResult0.0; 返回结果; } @重写公共AggregateResult addFactorCalDetail值,AggregateResult累加器{ acculator.setKeyvalue.getGroupKey; 累加器。添加结果; accumulator.setTimeSpanvalue.getTimeSpan; 回流蓄能器; } @重写公共聚合结果getResultAggregateResult累加器{ 回流蓄能器; } @覆盖公共聚合结果合并聚合结果a、聚合结果b{ 如果a.getKey.equalsb.getKey{ a、 setResulta.getResult+b.getResult; } 返回a; } } env.addSourcesource .keyByElement::getKey .windownew Element处理时间 .aggregatenew CountAggregate .addSinknew RedisCustomizeSinkredisProperties;
你不说源是什么,它会有自己的状态来保持。你也不会说有多少唯一的钥匙。随着唯一密钥数量的增加,即使是每个密钥的少量状态也会变得巨大。如果问题最终出现在聚合器状态增长的某个地方,您可以尝试将窗口逻辑拆分为一系列两个窗口,一个用于每小时聚合,另一个用于将每小时汇总聚合到所需的时间范围。

当您分配自定义窗口时,状态大小可能会很快失控。这主要是因为每个窗口需要保存属于它的所有记录,直到该窗口被聚合并最终被逐出。在代码中,似乎每个记录都创建了大量窗口

您没有指定您的用例,但我假设您实际上想要计算在给定的时间点上每个10毫秒大小的键有多少个事件。如果是这样,那么这就不是windows的直接用例

您要做的是:

将您的活动拆分为较小的活动。 按键和bin分组。 清点你的垃圾箱。 代码中的草图:

input.flatMap(element -> {
        ...
        for (long start = lastStart; start > timestamp - size; start -= slide) {
            emit(new KeyTime(key, start));
        }
    })
    .keyBy(keyTime -> keyTime)
    .count()
您可以在按键后应用windows以强制某些输出属性,例如等待几分钟,然后输出所有内容并忽略延迟事件

注意:KeyTime是一个简单的POJO,它持有密钥和bin time

编辑:在您的评论之后,解决方案实际上要简单得多

env.addSource(source)
    .keyBy(element -> new Tuple2<>(element.getKey(), element.getTime()))
    .count()
    .addSink(new RedisCustomizeSink(redisProperties));

如果您使用的是键控窗口,您可以使用RocksDB状态后端来减少堆上的压力我已经使用了RocksDB状态后端增量检查点,但是状态太大,10000个数据约为1g,这是无法忍受的;'⌒`你看到记忆中的状态快速增长了吗?测试环境k8s pod 1 core 2G检查点之间的最小暂停为1m,状态大小为快速增长这似乎更多的是一个评论而不是一个答案。我不理解两个窗口的方式,你能解释一下吗谢谢你的回答,我会尝试这种方式。在我的例子中,我想在不同的时间跨度内计算密钥。例如,该点流中有5个数据:key1、10min、data1、key1、10min、data2、key1、20min、data3、key1、20min、data4、key1、20min、data5;所以这一点的结果是key1,10min,2,key1,20min,3请参阅我更新的答案,以获得更简单的解决方案。顺便说一句,从实际用例开始提问总是有意义的。