我有一个批量数据流管道,我已经在我们的数据子集上运行了多次,大约有150k行输入。我现在尝试在大约300M行的完整数据集上运行。管道的一个关键部分执行输入记录的GroupByKey,从而产生(我相信)~100M密钥。
管道的相关部分如下所示:
// Results in map of ~10k elements, 160MB in size
PCollectionView<Map<String, Iterable<Z>>> sideData = ...
...
.apply(ParDo.named("Group into KV").of(
new DoFn<T, KV<String, T>>() { ... }
))
.apply("GBK", GroupByKey.create())
.apply(ParDo.named("Merge Values").withSideInputs(sideData).of(
new DoFn<KV<String, Iterable<T>>, V>() { ... }
))
我已经两次运行这个管道,并且每次工作在运行超过16小时后停止运行。我第一次使用10 n1-highmem-8
运行它,第二次使用6 n1-highmem-16
个实例运行它。
我可以从Dataflow作业控制台判断Group into KV
ParDo完成正常并输出101,730,100个元素,大小为153.67 GB。 GBK
转换的步骤详细信息表示在第一次和第二次尝试中分别添加了72,091,846和72,495,353个元素。此时GBK
转换仍处于运行阶段,但所有计算机上的CPU降至零并且管道有效停止。管道中的所有未来阶段都停止递增元素计数。我可以在机器上查看/ var / log / dataflow /下的各种日志,但似乎没有任何异常。云控制台和GC日志中的错误似乎并不表示内存问题。
此时我有点不知所措,知道下一步该做什么。我已经读过使用Combiner
而不是使用GroupByKey
可以产生更好的可伸缩性。通过一些重构,我可以使代码是可交换的,这样Combiner就是一个选项。我有点犹豫不决,因为每次尝试运行此管道时,浪费的云计算时间花费了大约250美元。
我的问题是:
有什么建议的方法可以找出管道在停止时正在做什么?我应该在java进程上执行kill -QUIT <pid>
以获取堆栈跟踪,如果是,那么它会去哪里?
有没有人知道为什么这条管道在没有任何错误或警告的情况下会突然失速?
上述工作的IDS:
答案 0 :(得分:2)
看起来一个工作人员可能会被卡住或需要很长时间才能在DoFn
之后运行GroupByKey
代码。最可能的原因是“热键”(具有比其他键更多的值)。您可以向DoFn
添加聚合器,并在运行时报告Iterable的大小,如下所示:
private static class MyDoFn extends KV<String, Iterable<T>>, V> {
private static final Logger LOG =
LoggerFactory.getLogger(FilterTextFn.class);
private final Aggregator<Long, Long> maxIterableSize =
createAggregator("maxIterableSize", new Max.MaxLongFn());
@Override
public void processElement(ProcessContext c) {
long numElements = 0;
for (T value : c.element().getValue()) {
maxIterableSize.addValue(numElements++);
if (numElements == 100) {
LOG.warning("Key {} has > 100 values", c.element().getKey());
}
... // the rest of your code inside the loop
}
}
}
以上将添加一个计数器,显示单个键上的最大元素数,并向Cloud Logging报告任何具有100个以上值的键(可随意调整阈值,因为这似乎是合理的 - 单个热键可能有许多多于其他任何键的元素。)
另一种可能性是代码中有DoFn
的某些内容在某些特定数据集上挂起或非常慢。您可以尝试连接到正在处理此项目的工作人员并查看其正在处理的内容(如您所述使用kill -QUIT <pid>
)。