在 GCS 上读取 Avro 文件时出现 OutOfMemoryError 异常

Posted

技术标签:

【中文标题】在 GCS 上读取 Avro 文件时出现 OutOfMemoryError 异常【英文标题】:OutOfMemoryError exception when reading Avro files on GCS 【发布时间】:2015-01-16 11:28:29 【问题描述】:

我将大小约为 650GB 的 BigQuery 数据集导出到 GCS 上的 Avro 文件,并运行数据流程序来处理这些 Avro 文件。但是,即使只处理一个大小约为 1.31GB 的 Avro 文件,也会遇到 OutOfMemoryError 异常。

我收到以下错误消息,似乎异常源于 AvroIO 和 Avro 库:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
        at org.apache.avro.io.BinaryDecoder.readString(BinaryDecoder.java:260)
        at org.apache.avro.io.ValidatingDecoder.readString(ValidatingDecoder.java:107)
        at org.apache.avro.generic.GenericDatumReader.readString(GenericDatumReader.java:348)
        at org.apache.avro.generic.GenericDatumReader.readString(GenericDatumReader.java:341)
        at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:154)
        at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:152)
        at org.apache.avro.generic.GenericDatumReader.readRecord(GenericDatumReader.java:177)
        at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:148)
        at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:139)
        at org.apache.avro.file.DataFileStream.next(DataFileStream.java:233)
        at org.apache.avro.file.DataFileStream.next(DataFileStream.java:220)
        at com.google.cloud.dataflow.sdk.runners.worker.AvroReader$AvroFileIterator.next(AvroReader.java:143)
        at com.google.cloud.dataflow.sdk.runners.worker.AvroReader$AvroFileIterator.next(AvroReader.java:113)
        at com.google.cloud.dataflow.sdk.util.ReaderUtils.readElemsFromReader(ReaderUtils.java:37)
        at com.google.cloud.dataflow.sdk.io.AvroIO.evaluateReadHelper(AvroIO.java:638)
        at com.google.cloud.dataflow.sdk.io.AvroIO.access$000(AvroIO.java:118)
        at com.google.cloud.dataflow.sdk.io.AvroIO$Read$Bound$1.evaluate(AvroIO.java:294)
        at com.google.cloud.dataflow.sdk.io.AvroIO$Read$Bound$1.evaluate(AvroIO.java:290)
        at com.google.cloud.dataflow.sdk.runners.DirectPipelineRunner$Evaluator.visitTransform(DirectPipelineRunner.java:611)
        at com.google.cloud.dataflow.sdk.runners.TransformTreeNode.visit(TransformTreeNode.java:200)
        at com.google.cloud.dataflow.sdk.runners.TransformTreeNode.visit(TransformTreeNode.java:196)
        at com.google.cloud.dataflow.sdk.runners.TransformHierarchy.visit(TransformHierarchy.java:109)
        at com.google.cloud.dataflow.sdk.Pipeline.traverseTopologically(Pipeline.java:204)
        at com.google.cloud.dataflow.sdk.runners.DirectPipelineRunner$Evaluator.run(DirectPipelineRunner.java:584)
        at com.google.cloud.dataflow.sdk.runners.DirectPipelineRunner.run(DirectPipelineRunner.java:328)
        at com.google.cloud.dataflow.sdk.runners.DirectPipelineRunner.run(DirectPipelineRunner.java:70)
        at com.google.cloud.dataflow.sdk.Pipeline.run(Pipeline.java:145)
        at com.htc.studio.bdi.dataflow.ActTranGenerator.main(ActTranGenerator.java:224)

对这个例外有什么建议吗?

谢谢!

【问题讨论】:

【参考方案1】:

您正在使用在本地计算机上运行的 DirectPipelineRunner。此模式完全在内存中运行,最适合在小型数据集上进行测试或开发。直接管道执行可能需要在内存中保留多个数据副本(取决于您的确切算法),因此我不建议将其用于大文件。而是指定 --runner=BlockingDataflowPipelineRunner 以通过 Dataflow 服务运行。


此信息与您的情况没有直接关系,但可能有助于其他人在使用 DataflowPipelineRunner 或 BlockingDataflowPipelineRunner 时遇到 OOM:

OutOfMemory 异常可能难以诊断,因为: (1) 内存耗尽的位置可能不是消耗大量内存的位置。 (2) 由于 Dataflow 优化管道的方式,来自管道不同逻辑组件的 ParDos 可能在同一个 JVM 中一起执行。 因此,您可能需要在工作日志中查找并置的 DoFn,以确定哪个 DoFn 实际占用了所有内存。

OOM 的一个常见原因是处理带有 DoFn 的 KV>,它试图将所有 V 保留在内存中(例如在 Collection 中)。这不适用于可能具有许多具有相同键的值的情况。

如果没有算法问题并且您只需要具有更多内存的工作人员,您可以调整 VM 实例类型,例如: --workerMachineType=n1-standard-4

【讨论】:

感谢您的回答,弗朗西丝!我忘记了 DirectPipelineRunner 应该在本地机器上运行。将运行器更改为 BlockingDataflowPipelineRunner 后,我可以通过从输入记录中提取一些字段来生成输出记录。但是,在运行完整的程序时,我遇到了另一个 OutOfMemoryError 异常:Java heap space even if --workerMachineType=n1-standard-4 is given。如何知道 Java 堆设置并在 Dataflow 服务中指定 Java 堆选项?

以上是关于在 GCS 上读取 Avro 文件时出现 OutOfMemoryError 异常的主要内容,如果未能解决你的问题,请参考以下文章

使用自定义列名将 Avro 文件加载到具有嵌套记录的 GCS

使用 pyspark 在 Jupyter notebook 中读取 avro 文件时遇到问题

性能:Google Dataflow 将 avro 文件写入 GCS

从 pubsub->bigquery 移动到 pubsub->gcs (avro)->bigquery

数据流 - 将 avro 对象存储到未知的 GCS 文件夹

在 Spark 2.0 中从 AVRO 写入镶木地板时出现 NullPointerException