我们正在运行一个ListState在300GB至400GB之间的作业,并且有时列表会增长到几千个。在我们的用例中,每个项目都必须具有自己的TTL,因此我们为该ListState的每个新项目创建一个新的Timer,并在S3上具有RocksDB后端。
当前大约有140+百万个计时器(将在 event.timestamp + 40days 触发)。
我们的问题是,作业的检查点突然卡住,或者非常缓慢(例如几小时内达到1%),直到最终超时。它通常在一段很简单的代码上停止(flink仪表板显示0/12 (0%)
,而前几行显示12/12 (100%)
):
[...]
val myStream = env.addSource(someKafkaConsumer)
.rebalance
.map(new CounterMapFunction[ControlGroup]("source.kafkaconsumer"))
.uid("src_kafka_stream")
.name("some_name")
myStream.process(new MonitoringProcessFunction()).uid("monitoring_uuid").name(monitoring_name)
.getSideOutput(outputTag)
.keyBy(_.name)
.addSink(sink)
[...]
更多信息:
state.backend.rocksdb.thread.num = 4
这是我们第二次想到拓扑运行得很好,每天有几百万个事件,并且突然停止检查点。我们不知道是什么原因造成的。
任何人都可以想到会突然导致检查点卡住的原因吗?
答案 0 :(得分:2)
一些想法:
如果您有许多同时或多或少触发的计时器,则这种计时器风暴将阻止发生其他任何事情-任务将循环调用onTimer,直到没有更多的计时器被触发为止,在此期间它们的输入队列将被忽略,检查点障碍也不会继续。
如果这是造成您麻烦的原因,则可以在计时器中添加一些随机抖动,以便事件风暴以后不会变成计时器风暴。重新组织事物以使用State TTL可能是另一种选择。
如果堆上有很多计时器,这会导致非常高的GC开销。这不一定会使工作失败,但会使检查点不稳定。在这种情况下,将计时器移至RocksDB可能会有所帮助。
此外:由于您使用的是RocksDB,因此以时间为键从ListState切换到MapState可以使您删除单个条目,而不必在每次更新后重新序列化整个列表。 (使用RocksDB,MapState中的每个键/值对都是一个单独的RocksDB对象。)以这种方式提高清除效率可能是最好的解决方法。