Spark之Core高级知识分享五(textFile+Shell+Tuning)

1.sc.textFile()源码讲解

textFile读取的是hdfs上的数据,调的底层就是hdfs读取数据API
1

2.shell脚本启动流程

Spark相关shell脚本是工业级脚本,在以后生产上若遇到写此类的服务脚本完全,可以借它们进行代码搬运

3.(Tuning Guidance)

spark生产中,调优是非常重要的一部分,好的调优可使得程序更加高效稳健,以下从常用的六个方面进行调优。

3.1数据存储级别改为MEMO_ONLY_SER

spark core中默认的数据存储级别即mem_only,可以设置为,mem_only_sec,这样大大减少内存的开销。

3.2.使用Kryo进行ser

java序列化速度慢且占用也大,而kryo更快且小。

  • 第一步:配置 , conf.set(“spark.serializer”,“org.apache.spark.serializer.KryoSerializer”),该配置可在spark-default中进行默认配置
  • 第二步:Regist,将需要序列化的对象注册到Kryo,虽然不注册也能用,但是相较于注册速度慢且大点儿
    如下是测试代码,实验数据100万条
package com.wsk.spark.tuning

import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}

/**
  * 使用kryo序列化数据对象,
  * 1、生产山spark.serializer这个配置直接写到spark-default配置文件中即可
  */
object KryoApp {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("Kyro App").setMaster("local[2]")
        conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
        conf.registerKryoClasses(Array(classOf[Logger]

    val sc = new SparkContext(conf)
    val logRDD = sc.textFile("data/cdnlog/input/CDN_click.log")
      .map(x => {
        val logFields = x.split(",")
        Logger(logFields(0), logFields(1), logFields(2), logFields(3),
          logFields(4), logFields(5), logFields(6), logFields(7))
      })
    //    val pesisitRDD = logRDD.persist(StorageLevel.MEMORY_ONLY)
    val pesisitRDD = logRDD.persist(StorageLevel.MEMORY_ONLY_SER)

    println("总条数" + pesisitRDD.count())
    
    Thread.sleep(2000000)
    sc.stop()
  }

  case class Logger(filed1: String, filed2: String, filed3: String,
                    filed4: String, filed5: String, filed6: String,
                    filed7: String, filed8: String)
}

测试结果:

存储方式大小
磁盘93.5M
内存560.4 MB
内存+java序列化116.2M
内存+kyro+不注册130.7M
内存+kyro+注册96.4M

从表中可以轻易看出,使用kryo方式进行序列化,占用的内存最少,非常接近数据在磁盘中的大小。
综上所述:生产中内存不够,应使用kryo方式序列化数据

3.3. leval of Parallelism

将RDD的分区数合理的设置为core的2~3倍

3.4 MemoryManager Tuning(内存管理优化)

这里介绍了内存管理的方式以及从源码解读两种内存的计算方式,生产中必要时可修改相关参数从而达到对内存管理优化。

3.4.1统一内存管理

spark1.5之后默认使用统一内存管理,spark能管理的内存通常被划分为两个部分:

  • Executor Memory:计算时占用内存,如在map、reduce、join时数据占据的内存。
  • Storage Memory:cache数据占用的内存。

Executor Memory和Storage Memory公用一个内存的region(统一内存管理),双方都可以互相的借用至全部的内存,但是当双方都资源紧张时Executor Memory可以驱逐部分Storage Memory到一定阀值(默认0.5),反之不可以,这种设计是保障计算能够进行下去。

3.4.2 两种MemoryManager占比源码解析

以下是对比静态以及统一两种内存管理,相关的代码片段:

# SparkEnv.scala
val useLegacyMemoryManager = conf.getBoolean("spark.memory.useLegacyMode", false)
    val memoryManager: MemoryManager =
      if (useLegacyMemoryManager) {
        new StaticMemoryManager(conf, numUsableCores)
      } else {
        UnifiedMemoryManager(conf, numUsableCores)
      }

# StaticMemoryManager 构造器
  def this(conf: SparkConf, numCores: Int) {
    this(
      conf,
      StaticMemoryManager.getMaxExecutionMemory(conf),
      StaticMemoryManager.getMaxStorageMemory(conf),
      numCores)
  }  
#getMaxStorageMemory
 private def getMaxStorageMemory(conf: SparkConf): Long = {
	 ....
    val memoryFraction = conf.getDouble("spark.storage.memoryFraction", 0.6)
    val safetyFraction = conf.getDouble("spark.storage.safetyFraction", 0.9)
    // // 10G * 0.6 * 0.9 =5.4G
    (systemMaxMemory * memoryFraction * safetyFraction).toLong
  }
#getMaxExecutionMemory
  private def getMaxExecutionMemory(conf: SparkConf): Long = {
  ....
    val memoryFraction = conf.getDouble("spark.shuffle.memoryFraction", 0.2)
    val safetyFraction = conf.getDouble("spark.shuffle.safetyFraction", 0.8)
    // 10G * 0.2 * 0.8 =1.6G
    (systemMaxMemory * memoryFraction * safetyFraction).toLong
  }


# UnifiedMemoryManager 构造器
  def apply(conf: SparkConf, numCores: Int): UnifiedMemoryManager = {
  	// (10G-300M)*0.6
    val maxMemory = getMaxMemory(conf)
    // storage分配的内存:onHeapStorageRegionSize = (10G-300M)*0.6*0.5
    //反之executor分配的内存:(10G-300M)*0.6*(1-0.5)
    
    new UnifiedMemoryManager(
      conf,
      maxHeapMemory = maxMemory,
      onHeapStorageRegionSize =
        (maxMemory * conf.getDouble("spark.memory.storageFraction", 0.5)).toLong,
      numCores = numCores)
  }

#getMaxMemory,
  private def getMaxMemory(conf: SparkConf): Long = {
    val systemMemory = conf.getLong("spark.testing.memory", Runtime.getRuntime.maxMemory)
    val reservedMemory = conf.getLong("spark.testing.reservedMemory",
      if (conf.contains("spark.testing")) 0 else RESERVED_SYSTEM_MEMORY_BYTES)
    val minSystemMemory = (reservedMemory * 1.5).ceil.toLong
    if (systemMemory < minSystemMemory) {
      throw new IllegalArgumentException(s"System memory $systemMemory must " +
        s"be at least $minSystemMemory. Please increase heap size using the --driver-memory " +
        s"option or spark.driver.memory in Spark configuration.")
    }
    // SPARK-12759 Check executor memory to fail fast if memory is insufficient
    if (conf.contains("spark.executor.memory")) {
      val executorMemory = conf.getSizeAsBytes("spark.executor.memory")
      if (executorMemory < minSystemMemory) {
        throw new IllegalArgumentException(s"Executor memory $executorMemory must be at least " +
          s"$minSystemMemory. Please increase executor memory using the " +
          s"--executor-memory option or spark.executor.memory in Spark configuration.")
      }
    }
    // 减去预留内存:10G-300M
    val usableMemory = systemMemory - reservedMemory
    //获取最大可管理内存,默认Memorymanager只能使用0.6的内存: (10G-300)*0.6
    val memoryFraction = conf.getDouble("spark.memory.fraction", 0.6)
    (usableMemory * memoryFraction).toLong
  }

代码中的10G是假设给于分配系统10G内存,这里用于计算的。

  • StaticMemoryManager:静态,老的内存管理,1.5以及1.5之前,从构造器源码可以看出,即使我们分配给了Executor 10G内存个,最大能用于计算的只有1.6G,用于存储5.4G,计算内存太少了会出现各种OOM情况,这时需要优化调整参数
  • UnifiedMemoryManager:动态,新的内存管理,1.5以后出现的,spark默认使用此种内存管理方式,10G内存可用于内存管理的是(10G-300M)*0.6,另外的0.4用于维持数据结构等,
3.4.3 Configs about MemoryManager

参考官网http://spark.apache.org/docs/latest/configuration.html 的Memory Management模块。

3.5 GC Tuning(垃圾回收优化)

gc的次数越多越会使程序夯住,尤其是发生full gc的时候,整个世界都暂停了。

3.5.1如何观察GC情况?

通常通过 Spark UI界面我们可以轻松的观看的每个executor的gc的时间情况。

3.5.2常见GC类型


eden满 :mirror gc
old 满::Full gc(Major GC),频繁的full gc会很影响程序性能的,一定要避免

3.5.2 tuning gc

spark gc tuning 的最终目的是将生命周期长的RDD对象放在老年代,生命周期短的RDD放在年轻代。

  • 若任务过程中Minor GC多full gc少,可将Eden区的大小占比调大一点儿,若oldgen 容易满(易发生full gc),可以将spark占jvm的内存比调低或者调低youngGEN占堆得大小,默认youngGEN占堆大小为1/3,可以考虑将老年区的大小调的比spark程序大。
  • GC是开销的成本和对象数成正比,若gc有问题可以尝试一下序列化方式去storage RDD 减少占用内存以及gc时间(对象数少了)看是否能解决,再不行就需要配置一些参数来调整内存的分配,减少gc频率尤其是full gc。

参看文章: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html

扩展1:MR中的Mapper以及Reducer接口范型参数有4个,mapp以及reduce方法参数有3个
扩展2:REPL指的是(Read-eval-print-loop)交互式解释器,读,求值、打印、循环
扩展3:uname 获取当前系统名称,exec XXX 就是执行xxx脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值