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. typora软件下载跟安装

    typora软件介绍 typora是一款文本编辑器 是目前非常火爆的文本编辑器 [下载地址](Typora 官方中文站 (typoraio.cn)) 安装操作 pj链接 注意:不要更新!!! 安装 路 ...

  2. 浅谈LCA问题(最近公共祖先)(四种做法)

    [模板]最近公共祖先(LCA) \(update \ 2023.1.3\) 新增了树链剖分 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \ ...

  3. 使用prometheus来避免Kubernetes CPU Limits造成的事故

    使用prometheus来避免Kubernetes CPU Limits造成的事故 译自:Using Prometheus to Avoid Disasters with Kubernetes CPU ...

  4. Git入门图文教程(1.5W字40图)🔥🔥--深入浅出、图文并茂

    01.认识一下Git!-简介 Git是当前最先进.最主流的分布式版本控制系统,免费.开源!核心能力就是版本控制.再具体一点,就是面向代码文件的版本控制,代码的任何修改历史都会被记录管理起来,意味着可以 ...

  5. Java 进阶P-5.3+P-5.4

    封装 增加可扩展性 可以运行的代码!=良好的代码 对代码做维护的时候最能看出代码的质量 如果想要增加一个方向,如down或up 用封装来降低耦合 Room类和Game类都有大量的代码和出口相关 尤其是 ...

  6. HelloWorld程序的代码编写-Hello World的编译运行

    HelloWorld程序的代码编写 程序开发步骤说明 开发环境已经搭建完毕,可以开发我们第一个Java程序了. Java程序开发三步骤:编写.编译.运行. 编写Java源程序 1. 在 d:\day0 ...

  7. Vue35 路由

    1 简介 vue-router是vue的一个插件,专门用来实现SPA应用.SPA也就是单页Web应用,特点是:整个应用只有一个完整的页面,点击页面中的导航链接不会刷新页面,只会做页面的局部更新,数据需 ...

  8. uboot启动过程 3

    uboot启动过程1描述到 _start -> reset ->  save_boot_params -> save_boot_params_ret ->  cpu_init_ ...

  9. 五:spring boot

    五.spring boot 通过springboot可以快速的搭建一个基于ssm框架的Java application,简单配置,自动装配. JavaConfiguration用java类来替代xml ...

  10. 【Go语言基础】slice

    一.概述 数组(Array)的长度在定义之后无法再次修改:数组是值类型,每次传递都将产生一份副本. 显然这种数据结构无法完全满足开发者的真实需求.Go语言提供了数组切片(slice)来弥补数组的不足. ...