Spark Components:

 角色组成:

  Driver :  由SparkContext创建,运行在main方法,负责资源申请与调度,程序分发,接收每个分区的计算结果
  Cluster manager:  获取集群内资源(模式standalone ,Mesos, YARN)的外部服务
  Worker node:  集群中能够运行计算程序的节点
  Executor:  work node上启动的一个进程,能够运行tasks,能在memory 或者 disk上存储数据,每个application都有自己的Executors
  Task:   发送给executor的一个执行单元(task是以thread形式执行)
  Job:     actions生成的多个任务组成的并行计算,每个action对应一个job
  Statge: 每个job划分为阶段性的小型任务集合(一个节点上顺序完成的一次计算)

架构说明:

1, 每个application都有自己的Executor进程,每个Executor可以多线程执行任务,存在整个application生命周期内,多个application之间互相独立(每个app对应一个jvm实例),多个spark application之间只能通过将数据写入外存储才能进行数据共享
    
2,spark计算层与集群管理模式无关,只要获取到Executor,并且Executor之间能够互相通信,它就能在集群中运行

3,driver负责监听接收Executor上的计算结果,driver必须确保其它Worker能够通过网络地址寻找到Executor,driver负责管理集群上的task分发,把task运行在较近的worker nodes上,如果执行task在远端的集群上,他会通过RPC方式提交operations到较近的节点运行task

Spark是以MapReduce为基础在其上进行功能扩展的集群计算框架,spark计算面向是RDD(resilient distributed dataset)数据源
RDD是编程抽象概念,代表可以跨机器进行分割的只读的数据集合,所有对数据操作都需通过RDD来处理。

RDD操作:

  create:通过hfile 或 scala collection作为数据源

  transformation:处理计算转换,map,flatmap,filter

  controler:对中间结果可存储在memory 或file供其它RDD数据复用

  actions:驱动RDD执行计算

Spark程序是一个惰性计算,通过action调用来驱动程序运行,代码被分发到集群上,由各个RDD分区上的worker来执行,然后结果会被发送回Driver进行聚合处理。
驱动程序创建一个或多个RDD,调用transform来转换RDD,然后调用reduce处理被转换后的RDD。在程序处理数据过程中使用的是pipleLine方式。

WordCount执行流程:

spark集群及逻辑划分:

 任务分发及调度:

Spark 存储管理:

  BlockManager管理内存,磁盘,堆外内存。其中主要内存使用分为execution和storage。execution主要在shuffles, joins, sorts 和 aggregations时使用,storage用于cache,集群中间数据传递。execution与storage内存可共享使用,没有execution任务,storage可以使用全部内存,当execution需要内存时,剔除storage到一定比例阈值空间。当有计算使用内存时,storage不可挤占execution的内存。

 数据缓存分类:
  persist:发生pipeline处理时内部存储
  checkpoint: 外存储点,存储执行会在下一个action算子执行后开始执行(即滞后执行)且会重复执行,需要结合persist使用
  broadcast:发生在driver,获取得到blockmanager ID,传到executor,执行时如没有数据则从blockmanager获取处理,下一次可直接使用

内存划分:300M空间系统预留,40%空间存储数据结构,spark元数据,应对不正常大对象产生的oom预留位,60%空间用于execution和storage

BlockManager组成结构:

spark RDD之间 Dependence类别:

spark shuffle组成:

Spark优化:

  1,相同的数据使用一个RDD:避免多次计算相同数据的RDD

  2,多次使用的RDD计算结果持久化:当一个RDD相同的算子执行多次时,为避免重复计算需要将计算结果进行缓存/持久化,加快下一次的计算
    持久化级别:
    MEMORY_ONLY(对象内存存储)
    MEMORY_AND_DISK(内存不够时写一部分到磁盘)
    MEMORY_ONLY_SER(序列化为字节数组存储在内存,相比memory_only对象存储更省空间,多出来的性能开销是序列化与反序列化的开销)
    MEMORY_AND_DISK_SER(序列化存储内存磁盘)
    DISK_ONLY(仅磁盘)

  策略选择

  (1)数据量较少优选MEMORY_ONLY,如果使用MEMORY_ONLY级别时发生了内存溢出,建议尝试使用MEMORY_ONLY_SER级别,此时每个partition是一个字节数组,降低了内存占用,如果RDD中数据量过多还是可能导致OOM

  (2)如果内存级别都无法使用,那么建议使用MEMORY_AND_DISK_SER,而不是MEMORY_AND_DISK。到了这一步说明RDD的数据量很大,内存无法完全放下,序列化后的数据比较少,可以节省内存和磁盘的空间开销。

  同时该策略会优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘。不建议使用DISK_ONLY和后缀为_2的级别,因为完全基于磁盘文件的数据的读写,会导致性能急剧降低,有时还不如重新计算一次所有RDD。

  (3)后缀为_2的级别所有数据都复制一份副本,并发送到其它节点上,数据复制及网络传输会导致较大的性能开销

  3,尽量减少shuffle计算:shuffle最耗性能,shuffle不同节点上相同的key拉取到一台机器进行聚合操作,涉及到磁盘IO和网络传输,byKey、join,distinct、repartition等算子会触发shuffle操作
  尽量使用reduceByKey或者aggregateByKey算子来替代掉groupByKey算子。因为reduceByKey和aggregateByKey算子在本地进行combiner较少拉取的数据量,而groupByKey算子是不会进行聚合全量数据会在集群的各个节点之间分发和传输,性能较差。

  4,算子优化:
  mapPartitions替代map:partition数据量不是很大时效率较高。一次调用会处理一个partition所有的数据,而不是一次函数调用处理一条,性能相对来说会高一些,但如果内存不够可能出现OOM异常

  foreachPartitions替代foreach:类似mapPartitions替代map 如将RDD中所有数据写MySQL等外存储时避免foreach频繁地创建和销毁数据库连接,每个partition使用一个connection,提高性能

  repartitionAndSortWithinPartitions替代repartition与sort:可以一边进行重分区的shuffle操作,一边进行排序,shuffle与sort两个操作同时进行

  mapValues/flatmapValues:当分区器,分区数没有变化,key没有变化,只对value进行转化可使用 mapValues->map 和 flatmapValues->flatmap 来避免产生不必要的shuffle操作

//例wordcount 对统计的value进行转换且进行分组
val words: RDD[String] = data.flatMap(_.split(" "))
val kv: RDD[(String, Int)] = words.map((_,1))
val res: RDD[(String, Int)] = kv.reduceByKey(_+_)
// val res01: RDD[(String, Int)] = res.map(x=>(x._1,x._2*10))
val res01: RDD[(String, Int)] = res.mapValues(x=>x*10)
val res02: RDD[(String, Iterable[Int])] = res01.groupByKey()
res02.foreach(println)

  broadcast外部变量:在算子函数中使用到外部变量时,默认情况下,Spark会将该变量复制多个副本,通过网络传输到task中,此时每个task都有一个变量副本,如果变量本身比较大,task比较多,会占用过多内存和传输性能问题

  广播后的变量会保证每个Executor内存中只有一份变量副本,同一个EXcutor内的task共享一个节省内存

  5,数据结构优化:
  1)减少java包装类的使用(object header 16byte)尽量使用基础类型替代
  2)使用array+ primitive types替换HashMap,List
  3)避免使用过多的小对象嵌套结构
  4)使用数值或枚举类型替换string作为key(string编码及长度等占用)
  5)内存小于32 GB 调整JVM flag -XX:+UseCompressedOops,使pointers 由8byte到4byte

  6,数据本地化:
    1)PROCESS_LOCAL,待处理的数据在相同的jvm实例内运行,这是最佳级别
    2)NODE_LOCAL,数据在一个节点(例如在同一个HDFS节点或者同一个节点的另一个Excutor上)
    3)NO_PREF,没有位置偏好,从任何地方访问一样快(Redis,mysql,HBase)
    4)RACK_LOCAL,同一机架上的节点
    5)ANY,数据不在同一个机架,在网络任意节点

  当task在等待executor执行超时时,有任何空闲executor上没有未处理数据的情况下,Spark 会切换到较低的local level执行task。

    两种执行策略:a), 等待,直到繁忙的 CPU 释放后,再次在这节点上启动task。    b) 立即在需要将数据传输到远端节点上启动新的task执行。
  Spark 通常是等待一段时间等待 CPU 释放,超时过期后它开始将数据从远处移动到空闲CPU的节点上执行。每个级别之间的等待超时时间可以单独配置,也可以一起配置在一个参数spark.locality

  相关参数:

  spark.locality.wait //本地进程内超时等待时间
  spark.locality.wait.node//本机超时等待时间
  spark.locality.wait.rack//同一机架超时等待时间
  spark.locality.wait.process//本地无引用的外部

  其它参数调优
  num-executors:作业总共要用多少个Executor进程。如果不设置的话,默认只会给你启动少量的Executor进程,此时Spark作业运行速度是非常慢,一般设置50~100个左右 太少无法充分利用资源,太多无法给予充分的资源

  executor-memory:每个Executor进程的内存。内存设置4G~8G较为合适,num-executors乘以executor-memory不能超过队列的最大内存

  executor-cores:每个Executor进程的CPU core数量。设置为2~4个较为合适

  driver-memory:Driver进程的内存。如果使用collect算子将RDD的计算结果数据全部拉取到Driver上处理,那么必须确保Driver的内存足够大

  spark.default.parallelism:设置每个stage的默认task数量,Spark默认设置的数量是偏少,不会使用足够的资源。如果task数量偏少的话,就会导致前面设置好的Executor的参数白费,无论有多少资源,只有1,2个task导致资源浪费,

  官网推荐设置原则是每个core2-3个task,总的为num-executors * executor-cores的2~3倍较为合适

  spark.storage.memoryFraction:RDD持久化数据在Executor内存中能占的比例,默认是0.5。有较多的RDD持久化操作,该参数的值可以适当提高一些,保证持久化的数据能够容纳在内存中。如发现作业由于频繁的gc导致运行缓慢,

  task执行用户代码的内存不够用,建议调低。作业中的shuffle操作比较多,而持久化操作比较少,建议调低

  spark.shuffle.file.bufferbuffer    文件溢写缓冲大小,默认32k
  spark.shuffle.memoryFraction   shuffle使用executor内存占比,默认0.2
  spark.reducer.maxSizeInFlight   shuffle read buffer 一次数据拉取量,默认48m
  spark.shuffle.spill.numElementsForceSpillThreshold   强制文件溢写数据的条目数阈值,默认integer最大值
  spark.shuffle.spill.initialMemoryThreshold   强制文件溢写数据量内存占用多少空间,默认5m

  数据倾斜问题:

     发现问题:

    1,sample countByKey()  wc查看结果。 

    2,在Spark Web UI查看一下当前这个stage各个task分配的数据量,执行时长

    解决方案:

    1,双重聚合:rdd进行key值随机前缀N 先进行一步combiner,然后去掉前缀再次combiner

    2,向上采样:对rdd内数据随机key前缀,较少数据的rdd内相同的key进行N(excutor core task)倍的扩容在进行join处理

    3,broadcast处理:如果有较少的数据量rdd与较大的rdd进行join则小的rdd进行broadcast后数据量多的rdd进行map join

    4,过滤异常数据,如某些无用冗余数据量较大,则先过滤处理

    5,提高并行度(缓解作用并未根本解决)RDD多分区,通过算子指定并行度,例如,reduceByKey(_+_,10),配置spark.default.parallelism

  推测计划问题:

    当spark task中0.75已执行完成,剩余task执行时间达到已完成task中位数的1.5倍,则spark会重新调度一个新的task执行此task未完成的任务。(spark默认关闭,map reduce有开启)

   导致问题:1,导致计算结果数据重复  2,如有数据倾斜发生会使task无法执行完 

   相关参数配置:

  spark.speculation//计划是否开启,默认false
  spark.speculation.interval//检测间隔 100ms
  spark.speculation.multiplier//执行缓慢时间界定,是多少倍的已执行完task中位数 1.5
  spark.speculation.quantile//所有已完成task中占总数的比例 0.75

Spark 要点总结及优化的更多相关文章

  1. 深入研究Spark SQL的Catalyst优化器(原创翻译)

    Spark SQL是Spark最新和技术最为复杂的组件之一.它支持SQL查询和新的DataFrame API.Spark SQL的核心是Catalyst优化器,它以一种新颖的方式利用高级编程语言特性( ...

  2. 【Spark 深入学习-08】说说Spark分区原理及优化方法

    本节内容 ------------------ · Spark为什么要分区 · Spark分区原则及方法 · Spark分区案例 · 参考资料 ------------------ 一.Spark为什 ...

  3. 服务端spark gbdt模型计算性能优化

    服务端使用训练出来的模型,spark模型计算第一步是实现spark模型加载. 线上服务对用户体验影响极大,故需要对模型使用进行优化. 1.多线程并发进行计算,线上两个服务.优化cpu 2.在扩召回集, ...

  4. spark.mllib源代码阅读-优化算法1-Gradient

    Spark中定义的损失函数及梯度,在看源代码之前,先回想一下机器学习中定义了哪些损失函数,毕竟梯度求解是为优化求解损失函数服务的. 监督学习问题是在如果空间F中选取模型f作为决策函数.对于给定的输入X ...

  5. spark(二)优化思路

    优化思路 内存优化 内存优化大概分为三个方向 1.所有对象的总内存(包括数据和java对象) 2.访问这些对象的开销 3.垃圾回收的开销 其中Java的原生对象往往都能被很快的访问,但是会多占据2-5 ...

  6. Spark Streaming实践和优化

    发表于:<程序员>杂志2016年2月刊.链接:http://geek.csdn.net/news/detail/54500 作者:徐鑫,董西成 在流式计算领域,Spark Streamin ...

  7. spark job运行参数优化

    http://www.cnblogs.com/LBSer/p/4129481.html 一.问题 使用spark join两张表(5000w*500w)总是出错,报的异常显示是在shuffle阶段. ...

  8. 46、Spark SQL工作原理剖析以及性能优化

    一.工作原理剖析 1.图解 二.性能优化 1.设置Shuffle过程中的并行度:spark.sql.shuffle.partitions(SQLContext.setConf()) 2.在Hive数据 ...

  9. 【转载】 Spark性能优化指南——基础篇

    转自:http://tech.meituan.com/spark-tuning-basic.html?from=timeline 前言 开发调优 调优概述 原则一:避免创建重复的RDD 原则二:尽可能 ...

  10. 【转】Spark性能优化指南——基础篇

    http://mp.weixin.qq.com/s?__biz=MjM5NDMwNjMzNA==&mid=2651805828&idx=1&sn=2f413828d1fdc6a ...

随机推荐

  1. docker 第二课

    构建nginx镜像 Dockerfile 指令: FROM centos:7.9.2009 #在整个dockfile文件中除了注释之外的第一行,要是FROM ,FROM 指令当前镜像的用于指定父镜像( ...

  2. 第一百一十七篇: JavaScript 工厂模式和原型模式

    好家伙,本篇为<JS高级程序设计>第八章"对象.类与面向对象编程"学习笔记   1.工厂模式 工厂模式是另外一种关注对象创建概念的创建模式. 它的领域中同其它模式的不同 ...

  3. paozhu c++ web framework 框架原理

    paozhu c++ web framework 框架原理 paozhu c++ web framework 使用 asio 网络库,如果用动态库方式还要boost库. paozhu框架 使用两个线程 ...

  4. 【Azure 云服务】为Azure云服务配置上自签名的SSL证书步骤

    问题描述 在使用Azure Cloud Service(云服务),默认的情况下都是使用的 HTTP 服务,通过 Visual Studio 2022 创建的默认 Cloud Service项目中,在S ...

  5. 使用APICloud AVM多端组件快速实现app中的搜索功能

    很多 APP 中都有搜索功能的需求,本文介绍怎么使用 avm 多端组件快速实现搜索功能. 在 APICloud 模块库搜索 animate-UISearchBar,添加到项目.多端组件需要下载源码,引 ...

  6. asp+vb.net解决调接口返回中文乱码问题

    1.问题描述 涉及语言:vb,vbscript,vb.net,asp 最近在工作中碰到了这样一个问题:需要调用一个接口解析简历文件中的关键信息.直接用postman测试该接口,接口返回值没问题,但一旦 ...

  7. 使用IDEA创建一个maven的web项目并部署到tomcat上

    目录 1.创建一个maven项目 2.为项目添加配置文件 3.创建一些类和jsp页面 4.将项目部署到tomcat 1.创建一个maven项目 打开IDEA,File--New--Project 选择 ...

  8. [编程基础] Python模块和包使用笔记

    本文探讨Python模块和Python包,这两种机制有助于模块化编程. 模块化编程是指将大型笨拙的编程任务分解为单独的,较小的,更易于管理的子任务或模块的过程.然后可以像构建模块一样将各个模块拼凑在一 ...

  9. Ajax+WCF+MySQL实现数据库部署并调用

    ​ 最近的数据库课程要求将MySQL数据库部署在服务器上,参考了大佬们的博客后,总结一下. 先放上参考的大佬们的博客. [原创经验分享]JQuery(Ajax)调用WCF服务 - 南宫萧尘 - 博客园 ...

  10. 动力节点——day07

    什么是异常? 异常是指在程序的运行过程中所发生的不正常的事件,它会中断正在运行的程序 Java中异常的存在形式? 异常在java中以类的形式存在,每一个异常类都可以创建异常对象 异常的继承结构图 编译 ...