Spark基础

spark是什么

Spark 计算模式也是属于MapReduce 模式。Spark框架是对MR框架的优化。
两者的对比

DiffMRSpark
数据存储HDFS的splitRDD对数据计算
编程范式Map Reduce 表达能力不强算子操作
计算过程落盘数据,IO代价大中间结果在内存
运行方式进程运行TaskTask以线程方式执行

大数据应用主要有

分类框架
离线处理MapReduce
交互式查询hive
流处理storm

spark 实现了 all in one。还提供了机器学习和图计算的库。

所以为什么Spark比MR快:

  1. 积极使用内存
  2. 线程方式执行任务,减少进程启动的消耗。

spark怎么用

运行模式有本地/伪分布式/集群
本地模式不用启动master/worker
集群以standalone/yarn/mecos 三种方式部署。
standalone 集群提交任务

  • client 模式
spark-submit --class org.apache.spark.examples.SparkPi \ 
$SPARK_HOME/examples/jars/spark-examples_2.11-2.4.5.jar 1000
  • Cluster 模式
spark-submit --class org.apache.spark.examples.SparkPi \ 
--deploy-mode cluster \ 
$SPARK_HOME/examples/jars/spark-examples_2.11-2.4.5.jar 1000

编程范式和怎么写代码

spark 定义了一个用于并行计算的集合RDD。它是

  1. 一组数据分区
  2. 对分区进行计算compute
  3. 维护RDD之间的依赖关系
  4. 对于kv 的RDD,包含一个partitioner
  5. 保存每个分区的(preferred location)

依赖:
在这里插入图片描述
注意:join 并不一定是宽依赖。
RDD可以缓存,下次使用就直接用,以加速计算。
RDD可以持久化数据(checkpoint),切断之前的血缘关系,如果后续出错,再用血缘关系来创建会非常慢,直接从checkpoint这里读数据可以提升性能。

编程范式:

  1. read data
  2. transform
  3. action
    transform 是惰性求值,action触发 真正的计算。

常见RDD算子

transform

map/mapPartition

map:每次处理一条数据
mapPartitions:每次处理一个分区的数据,分区的数据处理完成后,数据才能释放,资源不足时容易导致OOM
最佳实践:当内存资源充足时,建议使用mapPartitions,以提高处理效率

宽依赖的算子(shuffle):groupBy、distinct、repartition、sortBy、intersection、subtract

action

Action触发Job。一个Spark程序(Driver程序)包含了多少 Action 算子,那么就有多少Job;
collect() / collectAsMap()
stats / count / mean / stdev / max / min
reduce(func) / fold(func) / aggregate(func)
first / take /top/ foreach

aggregate 若有初始值,则结果是每个分区使用了初始值再加上全局汇总时使用初始值的总和。

常见PairRDD算子

transform

  1. mapValues / flatMapValues / keys / values,这些操作都可以使用 map 操作实现,是简化操作。对k-v中的每个k都对应v中的mapValues操作。

  2. PariRDD(k, v)使用范围广,聚合
    groupByKey / reduceByKey / foldByKey / aggregateByKey
    combineByKey(OLD) / combineByKeyWithClassTag (NEW) => 底层实现
    subtractByKey:类似于subtract,删掉 RDD 中键与 other RDD 中的键相同的元素

Demo

val rdd = sc.makeRDD(Array(("spark", 12), ("hadoop", 26), ("hadoop", 23), ("spark", 15), 
("scala", 26), ("spark", 25), ("spark", 23), ("hadoop", 16), ("scala", 24), ("spark", 16))) 

// groupByKey 
rdd.groupByKey().map(x=>(x._1, x._2.sum.toDouble/x._2.size)).collect 
rdd.groupByKey().map{case (k, v) => (k, v.sum.toDouble/v.size)}.collect 
rdd.groupByKey.mapValues(v => v.sum.toDouble/v.size).collect 
// reduceByKey 
rdd.mapValues((_, 1))
.reduceByKey((x, y)=> (x._1+y._1, x._2+y._2))
.mapValues(x => (x._1.toDouble / x._2)). collect() 
// foldByKey 
rdd.mapValues((_, 1)).foldByKey((0, 0))((x, y) => {
(x._1+y._1, x._2+y._2) }).mapValues(x=>x._1.toDouble/x._2).collect 

// aggregateByKey 
// aggregateByKey => 定义初值 + 分区内的聚合函数 + 分区间的聚合函数 
rdd.mapValues((_, 1))
.aggregateByKey((0,0))( (x, y) => (x._1 + y._1, x._2 + y._2), (a, b) => (a._1 + b._1, a._2 + b._2))
.mapValues(x=>x._1.toDouble / x._2). collect 

// 初值(元祖)与RDD元素类型(Int)可以不一致 
rdd.aggregateByKey((0, 0))( (x, y) => {println(s"x=$x, y=$y"); (x._1 + y, x._2 + 1)}, (a, b) => {println(s"a=$a, b=$b"); (a._1 + b._1, a._2 + b._2)} )
.mapValues(x=>x._1.toDouble/x._2).collect 

// 分区内的合并与分区间的合并,可以采用不同的方式;这种方式是低效的!
rdd.aggregateByKey(scala.collection.mutable.ArrayBuffer[Int]())( (x, y) => {x.append(y); x}, (a, b) => {a++b} ).mapValues(v => v.sum.toDouble/v.size).collect 

// combineByKey(理解就行) 
rdd.combineByKey( (x: Int) => {println(s"x=$x"); (x,1)}, (x: (Int, Int), y: Int) => {println(s"x=$x, y=$y");(x._1+y, x._2+1)}, (a: (Int, Int), b: (Int, Int)) => {println(s"a=$a, b=$b");(a._1+b._1, a._2+b._2)} ).mapValues(x=>x._1.toDouble/x._2).collect 
// subtractByKey val 
rdd1 = sc.makeRDD(Array(("spark", 12), ("hadoop", 26), ("hadoop", 23), ("spark", 15))) 
val rdd2 = sc.makeRDD(Array(("spark", 100), ("hadoop", 300))) rdd1.subtractByKey(rdd2).collect() 

// subtractByKey 
val rdd = sc.makeRDD(Array(("a",1), ("b",2), ("c",3), ("a",5), ("d",5)))
val other = sc.makeRDD(Array(("a",10), ("b",20), ("c",30))) rdd.subtractByKey(other).collect()

注意的是,groupByKey 没有map端combiner,shuffle 过程传输数据量大,效率低。ReduceByKey 在map端有combiner, shuffle过程效率高。

  1. 排序
    sortByKey:sortByKey函数作用于PairRDD,对Key进行排序

  2. join
    cogroup / join / leftOuterJoin / rightOuterJoin / fullOuterJoin

action

collectAsMap / countByKey / lookup(key)

spark demo 代码

代码上传执行

// 6、打包,使用spark-submit提交集群运行 // 
spark-submit --master local[*] --class cn.lagou.sparkcore.WordCount \ 
original-LagouBigData-1.0-SNAPSHOT.jar /wcinput/* 

spark-submit --master yarn --class cn.lagou.sparkcore.WordCount \
original-LagouBigData-1.0-SNAPSHOT.jar /wcinput/*

RDD 进阶

序列化

对于自定义的RDD操作,在Driver中初始化,在Executor中执行。涉及到进程间通信,就需要Ser/Deser 了。

------------------ demo -----------------------

RDD依赖关系

持久化和缓存

cache() == persist(StorageLevel.Memeory_ONLY)

容错机制checkpoint

通过将RDD写入高可靠的磁盘,主要目的是为了容错。实现方式时执行完任务后,新启线程从后往前找到checkpoint点后执行。所以高效的方式时checkpoint之前cache一下。

以下场景适合使用检查点机制:

  1. DAG中的Lineage过长,如果重算,则开销太大
  2. 在宽依赖上做 Checkpoint 获得的收益更大

RDD分区

只讨论分布式模式
spark.default.parallelism = max(应用程序持有executor的core总数, 2)
SparkContext 初始化时

// 从集合中创建RDD的分区数 
sc.defaultParallelism = spark.default.parallelism 
// 从文件中创建RDD的分区数 
sc.defaultMinPartitions = min(spark.default.parallelism, 2)

广播变量

使用广播变量的过程如下:

  1. 对一个类型 T 的对象调用 SparkContext.broadcast 创建出一个 Broadcast[T] 对象。 任何可序列化的类型都可以这么实现(在 Driver 端)
  2. 通过 value 属性访问该对象的值(在 Executor 中)
  3. 变量只会被发到各个 Executor 一次,作为只读值处理

在这里插入图片描述

累加器

累加器的作用:可以实现一个变量在不同的 Executor 端能保持状态的累加;
累计器在 Driver 端定义,读取;在 Executor 中完成累加;
累加器也是 lazy 的,需要 Action 触发;Action触发一次,执行一次,触发多次,执行多次;
累加器一个比较经典的应用场景是用来在 Spark Streaming 应用中记录某些事件的数量;

优化TopN

Spark 原理

四大部分

Driver:用户编写的 Spark 应用程序就运行在 Driver 上,由Driver 进程执行
Master:主要负责资源的调度和分配,并进行集群的监控等职责
Worker:Worker 运行在集群中的一台服务器上。负责管理该节点上的资源,负责启动启动节点上的 Executor
Executor:一个 Worker 上可以运行多个 Executor,Executor通过启动多个线程(task)对 RDD 的分区进行并行计算

SparkContext 三大组件

  1. DAGScheduler:负责将DAG划分成若干个Stage
  2. TaskScheduler:将DAGScheduler提交的 Stage(Taskset)进行优先级排序,再将 task 发送到 Executor
  3. SchedulerBackend:定义了许多与Executor事件相关的处理,包括:新的executor注册进来的时候记录executor的信息,增加全局的资源量(核数);executor更新状态,若任务完成的话,回收core;其他停止executor、removeexecutor等事件

shuffle 原理

RDD 编程优化

  1. RDD 复用
  2. RDD cache/persist
  3. filter
  4. 使用高性能算子
  • 避免使用groupByKey,根据场景选择使用高性能的聚合算子 reduceByKey、aggregateByKey
  • coalesce、repartition,在可能的情况下优先选择没有shuffle的操作
  • foreachPartition 优化输出操作
  • map、mapPartitions,选择合理的选择算子
  • 用 repartitionAndSortWithinPartitions 替代 repartition + sort 操作
  • 合理使用 cache、persist、checkpoint,选择合理的数据存储级别
  • 减少对数据源的扫描(算法复杂了)
  1. 合理的并行度
  • Spark作业中的并行度指各个stage的task的数量
  • 设置合理的并行度,让并行度与资源相匹配。简单来说就是在资源允许的前提下,并行度要设置的尽可能大,达到可以充分利用集群资源。合理的设置并行度,可以提升整个Spark作业的性能和运行速度
  1. 使用广播变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值