在带有窗口的GroupByKey之后,光束管道不产生任何输出,并且出现内存错误

时间:2019-04-12 16:31:03

标签: google-cloud-dataflow apache-beam spotify-scio

目的:

我想加载流数据,然后添加一个键,然后按键对它们进行计数。

问题:

当我尝试使用流传输方法(无界数据)加载和按密钥对大型数据进行分组时,Apache Beam Dataflow样条线会出现内存错误 。因为似乎数据是按组累积的,并且不会在触发每个窗口时更早触发数据。

如果我减小元素大小(元素数不会改变),它将起作用!因为实际上分组操作会等待所有数据被分组,然后触发所有新的窗口数据。

我对两者都进行了测试

beam版本2.11.0和scio版本0.7.4

beam 2.6.0版和scio 0.6.1版

重新生成错误的方法:

  1. 阅读包含文件名的Pubsub消息
  2. 逐行迭代器从GCS读取并加载相关文件
  3. 逐行平整元素(因此产生大约10,000个元素)
  4. 为元素添加时间戳(当前即时时间)
  5. 为我的数据创建一个键值(带有一些从1到10的随机整数键)
  6. 应用具有触发功能的窗口(如果行很小且没有内存问题,它将触发约50次)
  7. 按键计数(按键分组然后组合)
  8. 最后,我们应该有大约50 * 10个元素,分别通过窗口和键来表示计数(当行的大小足够小时,可以成功测试)

管道的可视化(步骤4至7):

enter image description here

按组分组步骤的摘要:

enter image description here

如您所见,数据是按组累积的,不会被发出。

窗口代码在这里:

val windowedData = data.applyKvTransform(
  Window.into[myt](
    Sessions.withGapDuration(Duration.millis(1)))
    .triggering(
      Repeatedly.forever(AfterFirst.of(
        AfterPane.elementCountAtLeast(10),
        AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.millis(1)))

      ).orFinally(AfterWatermark.pastEndOfWindow())

    ).withAllowedLateness(Duration.standardSeconds(100))
    .discardingFiredPanes()

)

错误:

org.apache.beam.runners.dataflow.worker.StreamingDataflowWorker$KeyCommitTooLargeException: Commit request for stage S2 and key 2 is larger than 2GB and cannot be processed. This may be caused by grouping a very large amount of data in a single window without using Combine, or by producing a large amount of data from a single input element.
    org.apache.beam.runners.dataflow.worker.StreamingDataflowWorker$KeyCommitTooLargeException.causedBy(StreamingDataflowWorker.java:230)
    org.apache.beam.runners.dataflow.worker.StreamingDataflowWorker.process(StreamingDataflowWorker.java:1287)
    org.apache.beam.runners.dataflow.worker.StreamingDataflowWorker.access$1000(StreamingDataflowWorker.java:146)
    org.apache.beam.runners.dataflow.worker.StreamingDataflowWorker$6.run(StreamingDataflowWorker.java:1008)
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    java.lang.Thread.run(Thread.java:745)

是否有解决方法,可以通过强制group-by发出每个窗口的早期结果来解决内存问题。

1 个答案:

答案 0 :(得分:1)

KeyCommitTooLargeException不是内存问题,而是protobuf序列化问题。 Protobuf对一个对象(google protobuf maximum size)的限制为2GB。数据流发现,管道中单个键的值大于2GB,因此无法对数据进行混洗。错误消息指示“这可能是由于在不使用合并的情况下将单个窗口中的大量数据分组或从单个输入元素产生大量数据引起的。”根据您的管道设置(即分配的随机密钥),后者更有可能。

管道可能已从GCS读取了一个大文件(> 2GB),并将其分配给了随机密钥。 GroupByKey要求进行密钥混洗操作,并且由于protobuf限制而导致Dataflow失败,因此卡在该密钥上并保持水印。

如果单个键的值较大,则可能需要减小值的大小,例如,压缩字符串,或将字符串拆分为多个键,或者首先生成较小的GCS文件。

如果较大的值来自多个键的分组,则可能需要增加键空间,以便每个按键操作的组最终将较少的键组合在一起。