Apache Flink中ListState与ValueState的性能选型疑问
Apache Flink中ListState与ValueState<List>的性能对比
在你的场景中——为每个Key存储最新100条记录,基于KeyedProcessFunction + RocksDB状态后端——两种实现都需要全量读取、修改后全量写入,但二者在性能上确实存在差异,具体分析如下:
存储结构差异
首先明确RocksDB对两种状态的存储方式:
ListState<Record>:Flink会将List中的每个Record作为独立的键值对存储,键的前缀是当前Key+状态名称+元素索引,每个元素单独序列化。ValueState<List<Record>>:将整个List序列化为一个完整的字节数组,作为单个键值对存储在RocksDB中。
读写操作性能
读操作
两种方式都需要全量读取所有记录:
ListState需要从RocksDB读取100个独立的小条目,涉及多次随机IO;ValueState只需读取一个包含100条记录的大条目,单次IO完成。
在记录数量固定为100条的场景下,二者的读性能差异不大,但ValueState的单次IO会略占优势。
写操作
全量更新时的差异更明显:
ListState需要先删除旧的100个条目,再写入新的100个条目,总共涉及200次RocksDB操作;ValueState仅需覆盖单个键值对,1次操作即可完成。
因此写操作上ValueState的性能更优。
检查点性能(关键差异点)
文档推荐使用ListState的核心原因在于增量检查点的支持:
- 当RocksDB开启增量检查点(这是生产环境的推荐配置)时,
ListState只会将发生变化的元素写入检查点,无需全量同步; - 而
ValueState<List<Record>>作为单个大值,每次检查点都需要将整个List全量序列化并写入,随着作业运行,检查点的开销会显著增加,甚至成为性能瓶颈。
总结建议
- 若作业未开启增量检查点、检查点频率极低,且对实时读写性能要求极高,
ValueState<List<Record>>的读写表现会更优; - 若作业使用RocksDB增量检查点(生产环境常规配置),
ListState在检查点阶段的性能优势会抵消读写时的微小差异,长期运行更稳定,也更符合Flink的状态设计规范。
两种实现代码示例
// 使用ListState的实现 ListState<Record> listState; Iterable<Record> records = listState.get(); List<Record> list = new ArrayList<>(); // 遍历迭代器并添加到list,加入新记录后截断到100条 records.forEach(list::add); list.add(newRecord); if (list.size() > 100) { list = list.subList(list.size() - 100, list.size()); } listState.update(list); // 使用ValueState的实现 ValueState<List<Record>> valueState; List<Record> list = valueState.value(); if (list == null) { list = new ArrayList<>(); } list.add(newRecord); if (list.size() > 100) { list = list.subList(list.size() - 100, list.size()); } valueState.update(list);
内容的提问来源于stack exchange,提问作者Liang
相关产品推荐
相关产品推荐

