我有使用State
和TimeService
对大量消息进行滑动计数的情况。滑动尺寸为1,窗口尺寸大于10小时。我遇到的问题是检查点需要花费很多时间。为了提高性能,我们使用增量检查点。但是当系统执行检查点时,它仍然很慢。我们发现大部分时间用于序列化用于清理数据的定时器。我们为每个键设置了一个计时器,总共有大约300M的计时器。
任何解决此问题的建议都将受到赞赏。或者我们可以用另一种方式进行计数?
--------------------------------------------
我想补充一些细节。滑动大小是一个事件,窗口大小超过10小时(每秒大约有300个事件),我们需要对每个事件做出反应。所以在这种情况下我们没有使用Flink提供的窗口。我们使用keyed state
来存储以前的信息。在timers
中使用ProcessFunction
来触发旧数据的清理作业。最后,dinstinct键的数量非常大。
答案 0 :(得分:3)
我认为这应该有效:
通过有效地执行类似keyBy(密钥模块100000)的操作,将Flink正在使用的密钥数量从300M减少到100K(例如)。然后,您的ProcessFunction可以使用MapState(其中键是原始键)来存储它需要的任何内容。
MapStates具有迭代器,您可以使用它来定期抓取每个映射以使旧项目过期。坚持每个键只有一个计时器的原则(如果你愿意的话,每个uberkey),这样你就只有100K的计时器。
更新:
Flink 1.6包括FLINK-9485,它允许定时器以异步方式进行检查点,并存储在RocksDB中。这使得Flink应用程序拥有大量计时器变得更加实用。
答案 1 :(得分:0)
如果不是使用计时器,而是为流的每个元素添加一个额外的字段来存储当前处理时间或到达时间,该怎么办?因此,一旦您想清理流中的旧数据,您只需使用过滤器运算符并检查它的旧数据是否会被删除。
答案 2 :(得分:0)
不是在每个事件上注册一个清算定时器,而是每隔一段时间只注册一次定时器,例如每1分钟一次?您只能在第一次看到密钥时注册它,并在onTimer
中刷新它。就像:
new ProcessFunction<SongEvent, Object>() {
...
@Override
public void processElement(
SongEvent songEvent,
Context context,
Collector<Object> collector) throws Exception {
Boolean isTimerRegistered = state.value();
if (isTimerRegistered != null && !isTimerRegistered) {
context.timerService().registerProcessingTimeTimer(time);
state.update(true);
}
// Standard processing
}
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Object> out)
throws Exception {
pruneElements(timestamp);
if (!elements.isEmpty()) {
ctx.timerService().registerProcessingTimeTimer(time);
} else {
state.clear();
}
}
}
Flink SQL Over
子句实现了类似的功能。你可以看看here