繁体   English   中英

Databricks/python - 创建强大的长期运行作业的最佳实践方法是什么

[英]Databricks/python - what is a best practice approach to create a robust long running job

我找不到一个很好的概述如何创建一个失败可能性中等的工作。

我是一位经验丰富的开发人员,但我对 databricks/spark 比较陌生。 虽然我可以通过编程解决问题,但我正在寻找最佳实践解决方案。

我的场景是从 Web API 中读取大量行。 运行该作业大约需要 36 小时。 在这 36 小时内,我很有可能在与 API 交互时遇到致命错误(超时、读取时断开连接、无效/意外的返回值等)。 虽然我可以越来越多地使我的工作对这些错误具有鲁棒性,但理想情况下,我不必再次运行整个工作来恢复。 理想情况下,我只需要运行失败的案例。

我的基本流程是这样的:

  • 读入一组精选的 ID(10 万个)
  • 对于每个 ID,调用 Web API 以获取详细信息
  • 将结果输出写入新表(ID + 详细信息)

我评估过的方法:

  1. 尝试以一揽子方式捕获所有错误并将故障输出到结果表中。 然后,恢复是在修补导致失败的任何原因后读取失败的行作为 ID 的来源。
  2. 将初始数据集划分为多个文件,并将计划在各个分区上工作的东西拼凑在一起。 如果其中一个项目失败,则重新运行单个分区。 全部成功后,汇总结果。 我认为这是可行的,但由于我对数据块的了解有限,它看起来很混乱。 我会做自己的分区和任务调度。 我希望有更好的方法。

我在脑海中想象的解决方案是这样的:

# Split the source table into 100 equal buckets
# Run only buckets 10,20,21 (presumably, those are the failed buckets)
# For each bucket, run the udf get_details
# If the bucket succeeds, put it's rows into aggregate_df.  Otherwise, into error_df
aggregate_df, error_df = df.split_table_evenly(bucket_count=100)
  .options(continue_on_task_failure=true)
  .filter(bucket=[10,20,21])
  .run_task_on_bucket(udf=get_details)

解决方案是使用支持流式查询检查点的Spark Structured Streaming Spark 指南非常详尽地描述了如何在不同场景中使用结构化流。 它没有明确涵盖我的用例——按行分解数据集——我将在这里进行描述。

基本方法是将流分解为 Spark 所称的“微批量”。 对于上面的场景,要理解的重要一点是,按时间触发批处理,而我想按批处理。 Spark 有一种支持数据批处理的提供程序——Kafka 提供程序,它可以基于偏移量进行批处理。 由于我不想通过 Kafka 运行我的数据,所以我选择不使用这种方法。

文件源确实有一个我们可以使用的工具:它能够使用maxFilesPerTrigger选项设置批处理中文件的数量限制 我也使用latestFirst首先处理最旧的文件,但这不是必需的。

source_dataframe = self.sparksession.readStream.option('maxFilesPerTrigger', 1) \
.option('latestFirst', True) \
.format('delta') \
.load(path)

由于这仅适用于整个文件,因此我需要在生成时限制文件的大小。 为此,我只是使用会生成合适的存储桶大小的密钥对数据集进行分区。

因为对于大多数用途来说,拥有许多文件并不是超级高效,所以我选择将此数据集写入两次,因此除非我在检查点,否则不要支付读取许多小文件的成本。 但是,这完全是可选的。

dataframe = # Some query
curated_output_dataframe = dataframe
generate_job_input_dataframe = dataframe.partitionBy('somecolumn')

curated_output_dataframe.write.format(my_format).save(path=my_curated_output_path)
generate_job_data_dataframe.write.format(my_format).save(path=my_job_data_path)

然后,您可以使用流式函数读取和写入数据集,几乎可以开始了。

source_dataframe = self.sparksession.readStream.option('maxFilesPerTrigger', 1).format(input_path).load(input_path)
query = dataframe.writeStream.format(output_path).start('output_path')
query.awaitTermination()

但是,在您以稳健的方式执行此操作之前,还有一些事情需要处理。

如果您希望能够恢复您的工作,则需要设置检查点位置。

sparkSession.readStream.option('checkpointLocation','/_checkpoints/some_unique_directory')

如果您不更改某些设置,Spark 将变得贪婪并读取您的所有输入文件。
有关更多详细信息,请参阅此答案

您应该估计您的费率并将起始目标费率设置为该费率。 我一开始就把我的设置得很低——spark 会从这个速率调整到任何它是可持续的。

sparksession.conf.set('spark.streaming.backpressure.enabled', True)
sparksession.conf.set('spark.streaming.backpressure.initialRate', target_rate_per_second)
sparksession.conf.set('spark.streaming.backpressure.rateEstimator', 'pid')
sparksession.conf.set('spark.streaming.backpressure.pid.minRate', 1)

除非您监视作业,否则 Spark 将永远运行。 Spark 假设流作业无限期地运行,并且在数据集耗尽之前没有明确支持运行。 您必须自己编写代码,不幸的是代码很脆弱,因为在某些情况下您必须检查作业状态消息。

有关更多详细信息,请参阅此答案 答案中的代码对我来说并不是 100% 有效 - 我在消息表达式中发现了更多案例。 这是我目前正在使用的代码(删除了日志记录和注释):

while query.isActive:
    msg = query.status['message']
    data_avail = query.status['isDataAvailable']
    trigger_active = query.status['isTriggerActive']
    if not data_avail and not trigger_active:
        if 'Initializing' not in msg:
            query.stop()
    time.sleep(poll_interval_seconds)

其他注意事项这些不是检查点微批次所必需的,但在其他方面很有用。

Spark 生成有用的日志——我发现在流执行中建立信任时,这两个日志很有用:

22/05/18 23:22:32 INFO MicroBatchExecution: Streaming query made progress:
22/05/18 23:24:44 WARN ProcessingTimeExecutor: Current batch is falling behind. The trigger interval is 500 milliseconds, but spent 5593 milliseconds

可以通过以下方式启用 PID 估计器的日志记录

sparksession.conf.set('log4j.logger.org.apache.spark.streaming.scheduler.rate.PIDRateEstimator', 'TRACE')

Spark 最近添加了 RocksDB 对流状态管理的支持,您需要明确启用。 它对我来说很顺利。

sparksession.conf.set('spark.sql.streaming.stateStore.providerClass', \                                    
    'org.apache.spark.sql.execution.streaming.state.RocksDBStateStoreProvider')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM