3.5 Spark的性能调整
虽然数据管道的高效执行是任务调度器优先考虑的,这是Spark驱动的一部分,有时Spark需要人为给出一些提示。Spark调度主要与两个参数有关:CPU和内存。当然其他资源(如磁盘和网络I/O)也在Spark性能方面发挥重要作用,但目前Spark、Mesos或YARN都不能主动管理它们。
要监控的第一个参数是RDD的分区数,可以从文件中读取RDD时明确指定。常见的Spark错误是分区太多,这样做需要提供更多的并行性。当任务开始/结束时间相对较小的情况下,这样做也可以工作。但是建议减少分区数,特别是在有聚合的情况时。
每个RDD的默认分区数和并行级别由spark.default.parallelism参数决定,可在$ SPARK_HOME/conf/spark-defaults.conf配置文件中定义此参数。具体的RDD的分区数也可以通过coalesce( )或repartition( )方法来显式地更改。
内核总数和有效内存不足会导致任务无法继续进行,通常会造成死锁。当从命令行调用spark-submit、spark-shell或PySpark时,可以用--executor-cores选项来指定每个执行器的内核数。也可以在之前讨论的spark-defaults.conf文件中设置相应的参数。如果内核数量设置得太大,调度器将无法在节点上分配资源,从而导致死锁。
类似地,可通过--executor-memory(或spark.executor.memory属性)选项来指定所有任务请求的堆大小(默认为1G)。如果执行器的内存设制得太大,调度器可能会被死锁,或只能调度节点上有限的执行器。
在计算内核和内存数量时,独立模式中隐含的假设是:Spark是唯一运行的应用程序,这可能是正确。当在Mesos或YARN下运行时,配置集群调度器很重要,它通过Spark驱动来调度执行器对资源的有效请求。相关的YARN属性有:yarn.nodemanager.resource. cpu-vcores和yarn.nodemanager.resource.memory-mb。YARN可能会多给一点请求的内存。YARN的yarn.scheduler.minimum-allocation-mb和yarn.scheduler.increment-allocation-mb属性分别控制着最小值和增量请求值。
JVM还可以使用堆以外的一些内存,例如,用于内部字符串和直接字节缓冲区。spark.yarn.executor.memoryOverhead的属性值被添加到执行器内存,以确定每个执行器对YARN的内存请求。它默认为max(384, .07 * spark.executor.memory)。
由于Spark需要在执行器和客户机节点之间传输数据,高效的序列化非常重要。第6章会介绍不同的序列化框架,但在默认情况下,Spark会使用Kryo来进行序列化,这要求按静态方法显式地注册类。如果运行时发现序列化错误,可能是因为相应的类没有被注册或Kryo不支持它,这种情形出现嵌套和复杂的数据类型。一般来说,若不能非常有效地完成对象序列化,建议避免在执行器之间传递复杂的对象。
驱动具有类似的参数:spark.driver.cores、spark.driver.memory和spark.driver.maxRe-sultSize。后者为从所有执行器收集的结果设置限制,是通过collect方法来进行收集的。让驱动进程不出现内存不足的异常很重要。另一种避免内存不足异常和后续问题的方法是修改管道返回的聚合(或过滤)的结果,也可改用take方法。