我正在尝试利用并行性来加速Top-10窗口操作。我的应用程序包含具有时间戳和密钥的事件(即Tuple2<Long,String>
),我的目标是为30分钟的翻滚窗口(使用事件时间)生成前10个最常用的密钥。为此,我的查询包括入口,窗口和聚合阶段。换句话说,我的代码需要执行以下操作:
DataStream<Tuple3<Long, String, Integer>> s = env
.readTextFile("data.csv")
.map(new MapFunction<String, Tuple3<Long, String, Integer>>() {
@Override
public Tuple3<Long, String, Integer> map(String s) throws Exception {
String[] tokens = s.split(",");
return new Tuple3<Long, String, Integer>(Long.parseLong(tokens[0]),
tokens[1], 1);
}})
.assignTimestampsAndWatermarks(
new AscendingTimestampExtractor<Tuple3<Long, String, Integer>>() {
@Override
public long extractAscendingTimestamp(Tuple3<Long, String, Integer> t) {
return t.f0;
}}).setParallelism(1);
以上是解析CSV文件中的数据并分配事件时间(即Ingress)的代码。我将并行性设置为1的原因是因为我需要事件显示为有序,以便我可以将它们分配给窗口。
接下来是棘手的部分,我尝试在生成正确(和有序)窗口结果的同时加快执行速度。
天真(连续)执行
以下代码提供了一种不使用任何并行性并生成串行流的解决方案:
DataStream<Tuple2<Long, String>> windowedTopTen = s
.windowAll(TumblingEventTimeWindows.of(Time.minutes(30)))
.apply(new SerialAggregation()).setParallelism(1);
其中SerialAggregation
扩展RichAllWindowFunction<Tuple3<Long, String, Integer>, Tuple2<Long, String>, TimeWindow>
,每个翻滚窗口Tuple2<Long, String>
(Long
是时间戳,String
包含前10个键)。
朴素的方法产生正确的结果,结果数据流按升序时间戳排序。不幸的是,它没有利用多线程,因此当输入数据是一些GB时,执行需要一段时间才能完成。
平行(更快)方法
在查看Flink关于Windows的文档后,我试图通过使用parallelism > 1
来实现前10个示例的更智能方法,同时为每个窗口生成正确的结果。因此,我发现我需要将s
转换为KeyedStream
,然后应用window()
转换。实质上:
DataStream<Tuple2<Long, String>> windowedTopTen = s
.keyBy(1)
.window(TumblingEventTimeWindows.of(Time.minutes(30)))
.apply(new PartialAggregation()).setParallelism(N);
其中PartialAggregation()
将为不同的时间戳产生(不相交的密钥集)的部分结果。换句话说,我的理解是,对于相同的时间戳t1
,我最终会partial_result_1
到partial_result_N
,其中N
是我设置的并行度。我的目标是聚合特定时间戳的所有部分结果(如t1
),但我不知道该怎么做。此外,当我能够将部分结果与匹配的时间戳组合时,我将如何生成数据流,其元组基于时间戳排序(如朴素解决方案生成的结果)。
问题
答案 0 :(得分:1)
首先,如果用Tuple3替换你的Tuple3,其中String是单个键,并且整数是计数器,那么将部分前10个结果组合到整个前10个中会更容易
然后你可以使用windowAll和聚合窗口函数来处理第二层窗口,该函数保留前10个键(整体)及其计数。