keyBy和两次求和的问题是什么

时间:2019-12-02 09:39:48

标签: scala apache-flink

下面是我写的简单代码:

val env = StreamExecutionEnvironment.getExecutionEnvironment

val list = new ListBuffer[Tuple3[String,Int,Int]]

val random = new Random()

for(x <- 0 to 4){
  if(random.nextBoolean()){
    list.append(("INSERT",2,1))
  } else {
    list.append(("UPDATE",2,1))
  }
}


val data = env.fromElements(list).flatMap(_.toList)


val keyed = data.keyBy(0).sum(1)

keyed.print()

val reKeyed = keyed.keyBy(0).sum(2)
reKeyed.print()

env.execute()

dataStream reKeyed 应该将 keyed 作为输入数据源。但是,打印的结果表明它们来自原始数据源。 如果第二次只调用 KeyBy 而不调用 sum 方法, 打印的结果是正确的。 所以有什么问题?

2 个答案:

答案 0 :(得分:0)

问题在于,如果您两次调用keyBy,则第二次调用将覆盖第一个调用,因此元素可能会出现在与以前不同的TaskManager上。 对于这种情况,您实际上是在说您实际上要使用DataStreamUtils.reinterpretAsKeyedStream,它应该完全按照您描述的方式工作,这意味着它不应更改先前键控的Datastream的分区。

答案 1 :(得分:0)

对于给定的代码段,我找不到任何错误,并怀疑您的期望与API不符。

我在源代码以及第一和第二分组求和中添加了一些打印语句。

source:1> (UPDATE,2,1)
source:1> (INSERT,2,1)
source:1> (UPDATE,2,1)
source:1> (UPDATE,2,1)
source:1> (INSERT,2,1)
first:3> (UPDATE,2,1)
first:2> (INSERT,2,1)
first:3> (UPDATE,4,1)
first:2> (INSERT,4,1)
first:3> (UPDATE,6,1)
second:2> (INSERT,2,1)
second:3> (UPDATE,2,1)
second:2> (INSERT,2,2)
second:3> (UPDATE,2,2)
second:3> (UPDATE,2,3)

如您所见,随机输入包含3条update和2条insert语句。因此,第一个keyBy的结果正确显示了update,6,1insert,4,1

现在,该结果用作第二个keyBy的输入,但是由于您正在第二列上求和,因此您第一项操作的结果将被丢弃。您可能希望第一个keyBy的“最终”总和被作为第二列总和的基础记录。但这实际上始终是第一个记录作为基础,这是流设置中唯一可行的选择。

您真正想要拥有的是同一分组依据中两个字段的总和。不幸的是,流API并没有捷径,但是实现自己很容易。

val keyed = data.keyBy(0)
    .reduce((tuple1, tuple2) => (tuple1._1, tuple1._2 + tuple2._2, tuple1._3 + tuple2._3))

keyed.print("first")

产生

source:4> (INSERT,2,1)
source:4> (INSERT,2,1)
source:4> (INSERT,2,1)
source:4> (UPDATE,2,1)
source:4> (INSERT,2,1)
first:3> (UPDATE,2,1)
first:2> (INSERT,2,1)
first:2> (INSERT,4,2)
first:2> (INSERT,6,3)
first:2> (INSERT,8,4)

由于对数据进行分组非常昂贵,因此该解决方案也更加有效。