云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

免备案CDN_三合一网站建设_试用

小七 141 0

apachespark@Scale:Facebook的60tb+生产用例

这是一个来自Facebook工程的apachespark社区博客。在这个技术博客中,Facebook在一个生产用例中分享了apachespark的tb级使用情况。Facebook经常使用分析来进行数据驱动的决策。在过去的几年里,用户和产品的增长促使我们的分析引擎对单个查询的数十兆字节的数据集进行操作。我们的一些批处理分析是通过久负盛名的Hive平台(Facebook在2009年为apachehive提供的)和我们的定制MapReduce实现Corona来执行的。Facebook还继续增加针对包括Hive在内的多个内部数据存储的ANSI-SQL查询的Presto足迹。我们支持其他类型的分析,如图形处理和机器学习(Apache Giraph)和流式处理(例如Puma、Swift和手写笔)。虽然Facebook提供的服务涵盖了广泛的分析领域,但我们不断地与开源社区互动,以便分享我们的经验,并向其他人学习。ApacheSpark由Matei Zaharia于2009年在加州大学伯克利分校(UC Berkeley)的AMPLab创立,后来在2013年为Apache贡献了力量。它是目前增长最快的数据处理平台之一,因为它能够在同一个API和底层计算引擎中支持流、批处理、命令式(RDD)、声明性(SQL)、图形和机器学习用例。Spark可以有效地利用更大的内存,跨整个管道优化代码,跨任务重用jvm以获得更好的性能。最近,我们觉得Spark已经成熟到可以将它与Hive相比较的一个批量处理用例。在本文的剩余部分中,我们将描述我们在扩展Spark以取代Hive工作负载时所获得的经验和教训。用例:实体排名的特性准备实时实体排名在Facebook上有很多种方式。对于一些在线服务平台,原始特征值是通过Hive离线生成的,数据加载到实时关联查询系统中。多年前建立的基于蜂箱的旧基础设施计算资源密集,维护起来很困难,因为管道被分成数百个较小的蜂箱作业。为了实现更新鲜的特性数据并提高可管理性,我们采用了一个现有的管道并尝试将其迁移到Spark。以前的配置单元实现基于配置单元的管道由三个逻辑阶段组成,每个阶段对应于数百个按实体标识划分的较小的配置单元作业,因为为每个阶段运行大型配置单元作业的可靠性较低,并且受每个作业的最大任务数的限制。这三个逻辑步骤可以概括如下:过滤掉非生产特征和噪音。在每个(实体标识、目标标识)对上聚合。将表分成N个碎片,并通过一个自定义二进制文件对每个碎片进行管道化,以生成一个用于在线查询的自定义索引文件。基于蜂巢的流水线构建索引大约花了三天时间。管理起来也很有挑战性,因为管道中包含数百个碎片作业,使得监控变得困难。没有简单的方法来衡量整个管道的进度或计算预计到达时间。考虑到现有的Hive管道的上述局限性,我们决定尝试用Spark构建一个更快、更易于管理的管道。Spark实现在全尺寸调试可能是缓慢的,具有挑战性的,而且是资源密集型的。我们从转换基于配置单元的管道中资源最密集的部分开始:第二阶段。我们从50GB的压缩输入开始,然后逐渐扩展到300GB、1TB,然后是20TB。每增加一个尺寸,我们就解决了性能和稳定性问题,但在20 TB的容量下进行试验,我们发现了最大的改进机会。在运行20TB的输入时,我们发现由于大量的任务,我们生成了太多的输出文件(每个文件的大小约为100MB)。在作业运行时的10个小时中,有3个小时用于将文件从暂存目录移动到HDFS中的最终目录。最初,我们考虑了两个选项:要么改进HDFS中的批重命名以支持我们的用例,要么配置Spark以生成更少的输出文件(由于这个阶段有大量的任务-70000-而很困难)。我们回避了这个问题,考虑了第三种选择。由于我们在管道的第二步中生成的tmp_table2表是临时的,并且仅用于存储管道的中间输出,所以我们实际上是在压缩、序列化和复制三个副本,用于单个读取工作负载,并使用TB的数据。相反,我们更进一步:删除两个临时表,将所有三个配置单元阶段合并到一个Spark作业中,该作业读取60tb的压缩数据,并执行90tb的洗牌和排序。最后的Spark作业如下:我们是如何为这份工作而努力的?当然,在第一次尝试时,甚至在第十次尝试时,为这么大的管道运行一次火花作业都不起作用。据我们所知,这是现实世界中最大的Spark作业,在shuffle数据大小方面(Databricks的Petabyte排序是在合成数据上)。我们对核心Spark基础设施和我们的应用程序进行了大量的改进和优化,才能运行此作业。这项工作的好处是,其中许多改进适用于Spark的其他大规模工作负载,我们能够将我们的所有工作贡献回开源ApacheSpark项目中—有关更多详细信息,请参阅JIRAs。下面,我们将重点介绍使实体排名管道之一能够部署到生产中的主要改进。可靠性修复处理频繁的节点重新启动为了可靠地执行长时间运行的作业,我们希望系统具有容错性并从故障中恢复(主要是由于正常维护或软件错误而导致的机器重新启动)。虽然Spark的设计可以容忍机器重新启动,但是我们发现了在它足够健壮来处理常见故障之前需要解决的各种错误/问题。使PipedRDD在获取失败时变得健壮(SPARK-13793):以前的PipedRDD实现不够健壮,无法获取由于节点重新启动而发生的故障,并且每当出现获取失败时,作业都会失败。我们对PipedRDD进行了更改,以优雅地处理获取失败,以便作业可以从这些类型的获取失败中恢复。可配置的最大获取失败次数(SPARK-13369):对于长时间运行的作业,由于机器重新引导而导致的获取失败的概率会显著增加。每个阶段允许的最大获取失败数是在Spark中硬编码的,因此,当达到最大值时,作业常常失败。我们做了一个更改,使其可配置,并将这个用例的值从4个增加到20个,这使得该任务在获取失败时更加健壮。中断更少的集群重启:长时间运行的作业应该能够在集群重启后继续运行,这样我们就不会浪费到目前为止完成的所有处理。Spark的可重启shuffle服务特性允许我们在节点重启后保留shuffle文件。在此基础上,我们在Spark driver中实现了一个特性,可以暂停任务的调度,这样作业就不会因为集群重新启动而出现过多的任务失败而失败。其他可靠性修复无响应驱动程序(SPARK-13279):在添加任务时,由于O(N^2)操作,SPARK驱动程序被卡住,最终导致作业被卡住并终止。我们通过删除不必要的O(N^2)操作来解决这个问题。过度的驱动程序推测:我们发现Spark驱动程序在管理大量任务时花费了大量的时间进行推测。在短期内,我们禁止了这项工作的投机活动。我们目前正在努力改变火花驱动器,以减少长期投机时间。大缓冲区整数溢出导致的TimSort问题(SPARK-13850):我们发现SPARK的不安全内存操作有一个导致TimSort内存损坏的错误。感谢Databricks的工作人员解决了这个问题,这使我们能够在大内存缓冲区上操作。调整shuffle服务以处理大量连接:在shuffle阶段,我们看到许多执行器在尝试连接到shuffle服务时超时。增加Netty服务器线程的数量(spark.shuffle.io.serverThreads)和backlog(spark.shuffle.io.backLog)解决了问题。FixSpark executor OOM(Spark-13958)(交易撮合商):一开始每个主机都要打包4个以上的reduce任务,这很有挑战性。Spark执行器内存不足,因为排序程序中存在导致指针数组无限期增长的错误。我们通过在没有更多内存供指针数组增长时强制将数据溢出到磁盘来解决此问题。因此,现在我们可以在不耗尽内存的情况下运行24个任务/主机。性能改进在实现上述可靠性改进后,我们能够可靠地运行Spark作业。在这一点上,我们把精力转移到与绩效相关的项目上,以充分利用Spark。我们使用Spark的度量和几个profiler来发现一些性能瓶颈。我们用来发现性能瓶颈的工具Spark UI度量:Spark UI提供了对特定阶段的时间消耗情况的深入了解。每个任务的执行时间被划分为子阶段,以便更容易地找到作业中的瓶颈。Jstack:Spark UI还为executor进程提供了按需Jstack函数,可用于查找代码中的热点。Spark-Linux-Perf/Flame-Graph支持:尽管上面的两个工具非常方便,但是它们不能为同时运行在数百台机器上的作业提供CPU配置的聚合视图。在每个作业的基础上,我们添加了对启用Perf分析的支持(通过libperfagent for Java symbols),并可以自定义采样的持续时间/频率。使用我们的in在执行器中聚合和显示分析样本