一分钟了解spark的调优

由于大多数 Spark 计算的内存性质, Spark 程序可能由集群中的任何资源( CPU ,网络带宽或内存)导致瓶颈。 通常情况下,如果数据有合适的内存,瓶颈就是网络带宽,但有时您还需要进行一些调整,例如  来减少内存的使用。 本指南将涵盖两个主要的主题:数据序列化,这对于良好的网络性能至关重要,并且还可以减少内存使用和内存优化。 我们选几个较小的主题进行展开。

序列化在任何分布式应用程序的性能中起着重要的作用。 很慢的将对象序列化或消费大量字节的格式将会大大减慢计算速度。 通常,这可能是您优化 Spark 应用程序的第一件事。 Spark 宗旨在于方便和性能之间取得一个平衡(允许您使用操作中的任何 Java 类型)。 它提供了两种序列化库:

Java serialization: 默认情况下,使用 Java ObjectOutputStream 框架的 Spark 序列化对象,并且可以与您创建的任何实现 java.io.Serializable 的类一起使用。 您还可以通过扩展 java.io.Externalizable 来更紧密地控制序列化的性能。 Java 序列化是灵活的,但通常相当缓慢,并导致许多类的大型序列化格式。

Kryo serialization: Spark 也可以使用 Kryo 库(版本2)来更快地对对象进行序列化。 Kryo 比 Java 序列化(通常高达10x)要快得多,而且更紧凑,但并不支持所有的 Serializable 类型,并且需要先注册您将在程序中使用的类以获得最佳性能。

您可以通过使用  初始化作业 并进行调用来切换到使用 Kryo conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")。此设置配置用于不仅在工作节点之间进行洗牌数据的串行器,而且还将 RDD 序列化到磁盘。 Kryo 不是默认的唯一原因是因为自定义注册要求,但我们建议您尝试在任何网络密集型应用程序。自从 Spark 2.0.0 以来,我们在使用简单类型,简单类型的数组或字符串类型对RDD进行混洗时,内部使用 Kryo serializer 。

Spark 自动包含 Kryo 序列化器,用于 Twitter chill 中 AllScalaRegistrar 涵盖的许多常用的核心 Scala 类。

要使用 Kryo 注册自己的自定义类,请使用该 registerKryoClasses 方法。

val conf = new SparkConf().setMaster(...).setAppName(...) conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2])) val sc = new SparkContext(conf)

所述 Kryo 文档 描述了更先进的注册选项,如添加自定义序列的代码。

如果您的对象很大,您可能还需要增加 spark.kryoserializer.buffer 。该值需要足够大才能容纳您将序列化的最大对象。

最后,如果您没有注册自定义类, Kryo 仍然可以工作,但它必须存储每个对象的完整类名称,这是浪费的。

有三个方面的考虑在调整内存使用:该量的存储你的对象所使用的(你可能希望你的整个数据集,以适应在内存中),则成本访问这些对象,并且开销垃圾收集(如果你有高成交物品条款)。

默认情况下, Java 对象可以快速访问,但可以轻松地消耗比其字段中的 “raw” 数据多2-5倍的空间。这是由于以下几个原因:

每个不同的 Java 对象都有一个 “object header” ,它大约是16个字节,包含一个指向它的类的指针。对于一个数据很少的对象(比如说一个Int字段),这可以比数据大。

Java String 在原始字符串数据上具有大约40字节的开销(因为它们存储在 Char 数组中并保留额外的数据,例如长度),并且由于 UTF-16 的内部使用而将每个字符存储为两个字节 String 编码。因此,一个10个字符的字符串可以容易地消耗60个字节。

公共收集类,例如 HashMap 和 LinkedList ,使用链接的数据结构,其中每个条目(例如: Map.Entry )存在”包装器”对象。该对象不仅具有 header ,还包括指针(通常为8个字节)到列表中的下一个对象。

原始类型的集合通常将它们存储为”盒装”对象,例如: java.lang.Integer。

本节将从 Spark 的内存管理概述开始,然后讨论用户可以采取的具体策略,以便在他/她的应用程序中更有效地使用内存。具体来说,我们将描述如何确定对象的内存使用情况,以及如何改进数据结构,或通过以串行格式存储数据。然后我们将介绍调整 Spark 的缓存大小和 Java 垃圾回收器。

Spark 中的内存使用大部分属于两类:执行和存储。执行存储器是指用于以混洗,连接,排序和聚合计算的存储器,而存储内存是指用于在集群中缓存和传播内部数据的内存。在 Spark 中,执行和存储共享一个统一的区域(M)。当没有使用执行存储器时,存储器可以获取所有可用的存储器,反之亦然。如果需要,执行可以驱逐存储,但只有在总存储内存使用量低于某个阈值(R)之前。换句话说, R 描述 M 缓存块永远不会被驱逐的区域。由于实施的复杂性,存储不得驱逐执行。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxydd.html