天下武功-唯快不破-Spark

写在前面

最近抽时间在看hadoop权威指南以及spark快速大数据分析两本书,整理了一些知识点。需要的请点赞收藏。


知识点整理

什么是Spark?

spark是一个用来实现快速而且通用的集群计算平台。

在速度方面,spark拓展了广泛使用的MR计算框架,而且更高效的支持更多计算模型,例如交互式查询和流式处理。由于spark是基于内存计算,所以速度比MR会快很多,大约有100倍


spark和hadoop作业的区别

  • Hadoop
    • hadoop中一个mr程序就是一个job,而一个job里面可以有一个或者多个Task,Task又可以去分位Map Task和Reduce Task!
    • MapReduce中的每个Task分别在自己的进程中运行完时,进程也就结束
  • Spark
    • Application:spark-submit提交的程序(由一个driver program和多个job组成)
    • Driver:完成任务的调度以及和executor和cluster Manager进行协调
      1. Driver Program时Spark的核心组件
      2. 负责构建SparkContent
      3. 将用户提交的job变成DAG图(类似数据处理的流程图)
      4. 根据策略将DAG图划分成多个stage,再根据分区从而生成一系列的tasks
      5. 根据tasks要求向RM申请资源
      6. 提交任务并且检测任务状态
    • Executor:每个Spark executor作为一个YARN容器(container)运行。真正执行task的单元,一个work node上可以有多个executor。
    • Job(由多个stage组成):和MR的job不一样,MR中主要是Map和Reduce的job,而Spark的job其实更好区别,一个Action算子(行动操作)就算一个job。比如说count()、collect()
    • Task:Task是Spark中的最小执行单元。RDD一般是带有partitions的,每个partition在一个executor上的执行可以认为是一个Task
    • Stage(对应一个taskset):是spark中独有的。一般而言一个job会切换成一定数量的stage。各个stage之间按照顺序执行
    • taskset:对应一组关联的相互之间没有shuffle依赖关系的Task组成

hadoopMR:多进程模型

  • 调度慢,启动Map、reduce太耗时
  • 计算慢,每一步都要保存中间结果落到磁盘
  • API抽象简单,只有Map和reduce两个原语
  • 缺乏作业流的描述,一项任务需要进行多轮MR处理
  • 但是相对来说稳定,因为是进程

Spark相对于MR解决了什么问题

  • 最大化利用内存cache。中间结果放内存,加速迭代。例如某结果集放内存中,加速后续查询和处理,解决运行慢的问题(减少对hdfs的依赖)
    # 原始查询
    select name,max(score) from stu where age > 18 group by name;
    select name,max(score2) from stu where age > 18 group by name;
    # Cache stu
    select * from stu where age > 18
    rdd.registerastable(cachetable)
    # 改造sql
    select name,max(score) from cache_table group by name;
    select name,max(score2) from cache_table group by name;
    
  • 丰富的算子
    • 转化操作(Transformations)
    • 行动操作(Actions)
  • 完整的作业描述
    例如下面这个wordcount,将整个作业穿起来。关键是这3行。可以立即解释。不像MR那样,需要实现多个map和reduce脚本,解决MR缺乏作业流描述问题
    val file = sc.textFile(hdfs://input)
    val counts = file.flatMap(_.split(" ")).map((_,1)).reduceByKey(_ + _).saveAsTextFile(hdfs://output)
    

RDD(弹行分布式数据集):

  • 本质:数据集的描述(只读的、可分区的分布式数据集),而不是数据集本身
  • 关键特征
    1. 计算结果保存内存中,控制数据的划分
    2. 记录数据而不是数据本身的变换,保证容错
    3. 懒操作,执行action算子的时候才操作
    4. 瞬时性:用的时候才产生,用完就释放
  • 创建方式
    1. 共享文件系统获取,如HDFS
      val a = sc.textFile("/data/1.txt")
    2. 通过现有的RDD转换得到
      val b = a.map((_, 1))
    3. 定义一个scala数组
      val c = sc.parallelize(1 to 10, 1)
    4. 由一个已经存在的RDD通过持久化操作生成
      val d = a.persist()
      a.saveAsHadoopFile("/output")
  • 操作类型(算子)
    1. Transformations

      map() : 数据处理:来一条处理一条
      filter() : 过滤器
      flatMap() :一到多数据打平的关系
      sample() : 采样
      groupByKey() : 指定key做聚合
      reduceByKey() :对key的value做计算
      union() :去重合并
      join():两表聚合
      crossProduct():内积
      sort():排序
      partitionBy():分桶

    2. Actions

      count():聚合总数
      collect():明文输出
      reduce():精简聚合
      lookup(5):Top5
      save():保存数据

*groupByKey*
map
union
union
*join*
RDD3
RDD4
RDD1
RDD2
RDD6
RDD5
RDD7

如上图,加*号的都是宽依赖,其它是窄依赖
(rddA => rddB)

  • 宽依赖: B的每个partition依赖于A的所有partition

    比如groupByKey、reduceByKey、join……,由A产生B时会先对A做shuffle分桶

  • 窄依赖: B的每个partition依赖于A的常数个partition

    比如map、filter、union……

spark参数调优

  • Executor的内存分为三块:

    1. 程序本身,默认占executor总内存的20%
    2. shuffle类算子,执行内存默认也是占用20%
    3. 让RDD持久化时候使用,cache、presist、broadcast,默认占用executor总内存的60%
  • Task的执行速度和每个executor进程的CPU Core数量有直接关系,一个CPU Core同一时间只能执行一个线程,每个executor进程上分配到的多个task,都是以task一条线程的方式,多线程并发运行的。如果CPU Core数量比较充足,而且分配到的task数量比较合理,那么可以比较快速和高效地执行完这些task线程

  • 参数调优

    • num-executors:该作业总共需要多少executor进程执行

        	-- 建议每个作业设置50-100个左右
      
    • executor-memory:设置每个executor进程的内存, num-executors* executor-memory代表作业申请的总内存量(尽量不要超过最大总内存的1/3~1/2)

       	-- 设置4G到8G合适 
      
    • executor-cores:每个executor进程的CPU Core数量,该参数决定每个executor进程并行执行task线程的能力,num-executors* executor-cores代表作业申请总CPU core数(不要超过总CPU Core的1/3~1/2 )

       	-- 建议:设置2~4个较合适
      
    • driver-memory:设置driver进程的内存

       -- 建议:通常不用设置,一般1G就够了,若出现使用collect算子将RDD数据
       全部拉取到Driver上处理,就必须确保该值足够大,否则OOM内存溢出 
      
    • spark.default.parallelism:每个stage的默认task数量

       	-- 建议:设置500~1000较合适,默认一个HDFS的block对应一个task,Spark默认值偏少,这样导致不能充分利用资源
      
    • spark.storage.memoryFraction:设置RDD持久化数据在executor内存中能占的比例,默认0.6,即默认executor 60%的内存可以保存持久化RDD数据

       	-- – 建议:若有较多的持久化操作,可以设置高些,超出内存的会频繁gc导致运行缓慢 
      
    • spark.shuffle.memoryFraction:聚合操作占用内存比例,默认0.2

       	-- – 建议:若持久化操作较少,但shuffle较多时,可以降低持久化内存占比,提高shuffle操作内存占比
      

spark开发调优

  • 避免创建重复的RDD,重复RDD极大的浪费内存
  • 尽可能的复用同一个RDD,可以减少算子的执行次数
  • 对多次使用的RDD进行持久化处理
    • 每次对一个RDD执行一个算子操作时,都会重新从源头处理计算一遍,计算出那个RDD出来,然后进一步操作,这种方式性能很差
    • 对多次使用的RDD进行持久化,将RDD的数据保存在内存或磁盘中,避免重复劳动
    • 借助cache()和persist()方法
      val a = sc.textFile("/input/data.data").cache
      
      val b = sc.textFile("/input/data.data").persist(StorageLevel.MEMORY_AND_DISK_SER)
      // 默认使用MEMORY_AND_DISK_SER的方式(内存充足以内存持久化优先,_SER表示序列化)
      
    • 避免使用shuffle算子,因为spark运行过程,最消耗性能的就是shuffle过程
      // 1. shuffle
      val rdd3 = rdd1.join(rdd2)
      
      // 2. 不用shuffle
      val rdd2_data = rdd2.collect()
      val rdd2_data_broadcast = sc.broadcast(rdd2_data)
      val rdd3 = rdd1.map(rdd2_data_broadcast .......)
      
    • 使用map-side预聚合的shuffle操作
      • 一定要使用shuffle的,无法用map类算子替代的,那么尽量使用map-site预聚合的算子
      • 类似于MR中的combiner
      • 可能的情况下使用reduceByKeyaggregateByKey算子替代groupByKey算子,因为reduceByKey或aggregateByKey算子会使用用户自定义的函数对每个节点本地相同的key进行预聚合,而groupByKey算子不会预聚合

spark常用组件

  1. Spark Core : 基于RDD提供的操作接口,利用DAG进行统一的任务规划
  2. Spark SQL:Hive的表 + Spark。通过把Hive的HQL转化为Spark DAG计算来实现
  3. Spark Streaming:Spark的流式计算框架
  4. MLIB:Spark的机器学习库,包含常用的机器学习算法
  5. GraphX:Spark图并行操作库
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值