Hadoop(四)—— Saprk笔记

什么是spark?
基于内存一站式快速的计算框架


spark下面有哪些产品?
spark core --> spark rdd , spark核心编程,MapReduce
spark sql --> hive
spark streaming --> storm , 流式实时计算
spark mllib --> 机器学习,人工智能  核心是算法 --> 核心是数学 --> 概率论 , 线性代数,高数(微积分) 关系不大
spark graphX --> 图计算 算法


为什么spark要比mr更快?
1.基于内存的,内存的读写速度大大超出磁盘。spark大部分计算都是放在内存里面,但有些操作是无法避免的,spark也有shuffle,最后spark的结果也得落地,落地就得走磁盘
2.spark是迭代式的计算,内部有很多的优化手段,DAG,切分STAGE这套机制能够优化计算的性能。


spark SQL 和 hive的关系?
hive首先本质来说是一个数据仓库,里面存放了很多数据
其次hive里面可以通过写sql做一些数据的离线批处理,运行的是MR
sparkSQL肯定不能当数据仓库,也是通过写sql的方式,做数据的离线计算,底层运行的RDD,讲解sparkSQL有一个重点,就是sparkSQL 和 hive的整合。sparkSQL从hive里面获取数据


sparkStreaming和storm的关系?
storm很多优点,比较老了,如果是纯实时(全部是实时的业务),现在有一个更优秀的解决方案:flink,在绝大部分场景下,sparkStreaming是可以取代storm
storm实时性,容错性都比sparkStreaming更优秀
相比于sparkStreaming不足的地方:吞吐量不如,sparkStreaming可以方便的和spark sql,spark core,spark mllib无缝的整合


spark开发语言的选择问题?
最主流的是scala,如果是单纯的大数据项目,scala跟合适,缺点:会scala人还是太少,第二,scala和第三方对的架构整合起来困难
java是目前最主流的开发语言,整合第三方架构简单,用java写spark,跟适合一些复杂大数据平台项目
java会把代码实现,scala也会实现
python更多的用在机器学习,python语言的库 特别多,机器去学习算法库也很多


搭建spark
1、上传spark 安装包  spark-2.1.0-bin-hadoop2.6.tgz
2. 解压  tar -zxvf /home/hadoop/soft/spark-1.6.1-bin-hadoop2.6.tgz -C /home/hadoop/apps/
3.修改配置文件 spark目录conf 目录下,slaves  spark-env.sh
slaves 
--------
beicai02
beicai03
beicai04


一台主节点(master),3台从节点(worker)
spark-env.sh
----------------
export JAVA_HOME=//usr/local/jdk1.7.0_45
export SPARK_MASTER_IP=beicai01
export SPARK_MASTER_PORT=7077


// 后续使用sparkSQL 等,需要加入一些新的配置,后续再说
4.分发安装的程序到其他的节点 
scp -r /home/hadoop/apps/spark-1.6.1-bin-hadoop2.6 beicai02:/home/hadoop/apps
scp -r /home/hadoop/apps/spark-1.6.1-bin-hadoop2.6 beicai03:/home/hadoop/apps
scp -r /home/hadoop/apps/spark-1.6.1-bin-hadoop2.6 beicai04:/home/hadoop/apps




 spark Web UI:
URL: spark://beicai01:7077    spark  master地址
REST URL: spark://beicai01:6066 (cluster mode)  采用cluster模式,默认的端口
Alive Workers: 4             有4个worker,worker是用来做计算的
Cores in use: 4 Total, 0 Used  在集群里面有多少的cpu core 可以使用
Memory in use: 4.0 GB Total, 0.0 B Used  在集群里面有多少内存可以使用 
Applications: 0 Running, 0 Completed  应用的运行情况,有多少个应用运行已经完成,有多少个正在运行中
Drivers: 0 Running, 0 Completed   有多少dirver运行完成,和正在运行
Status: ALIVE   状态是存活状态 


spark 运行 脚本:
/home/hadoop/apps/spark-1.6.1-bin-hadoop2.6/bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://beicai01:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
/home/hadoop/apps/spark-1.6.1-bin-hadoop2.6/lib/spark-examples-1.6.1-hadoop2.6.0.jar \
100


RDD
什么是RDD?
(Resilient Distributed Dataset):弹性式的分布式的数据集合
RDD本质是一个集合,集合里面就放了很多的元素
RDD是分布式,这个集合在很多的节点上都有其中的一小片
RDD是弹性式,数据优先放在内存里面,内存的读写性能比磁盘要好的多,但是价格要贵很多。所以容量不会很大,如果数据放不下,就把存放在磁盘里面






RDD特性:
1)一组分片
分片数决定了并行计算的数量,这个分片数量不能太少,一般情况下在生产环境中,设150-400之间都比较合适,实际得看你的集群资源(cpu)
RDD的分片的数量和worker的数量没有关系
2)一个计算每个分区的函数
spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数。compute函数会对迭代器进行复合,不需要保存每次计算的结果。这里说的函数,就是算子
3)RDD之间的依赖关系
RDD每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,spark可以通过这个以来关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算
丢失数据,恢复数据是一种容错机制,依赖链条-->lineAge(血缘),作用就是容错,但是在某些情况下,这种恢复数据的机制成本非常高。有一些不完美的地方,还有一种容错,checkPoint
4)一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
作用就是:可以指定上一个RDD的每一条数据通过我们自己定义的规则(自定义分区)到下一个RDD的哪个分区partition里面去。能够使用分片函数的RDD 都是k-v类型的RDD,因为这个分片函数,操作的就是key。非K-V类型的RDD说分区函数没有意义。


5)一个列表,存储每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。






大数据这种技术在设计的时候,都会考虑容错,hdfs的如何容错??
哪些数据会丢失?Namenode的数据丢失,可以通过镜像恢复过来,xxx.edit 
DataNode的数据丢失问题??是有副本的,设置有多少个副本,而且只要有副本的这种容错机制,每个副本不可以分配在相同的节点上。
Kakfa也有副本,在kakfa里面创建一个topic,设置有多少个分片,每个分片有多少副本,如果你在设置这个副本数量的时候如果超过了broker的数量,会报错。


大数据里面的所有的容错:都是两种套路
1. 一种套路就是副本机制---默认情况spark 不是这种副本机制的思想。 
2. 还有一种套路就是记录操作日志机制:默认情况下的一种容错的思想


运行过程中堆栈内存太小 sparkContext地方报错?
eclipse:
在window-->preferences-->java-->Installed JREs-->Edit-->Default VM arguments 添加:-Xms258m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m
IDEA:
Run-->Edit Configurations-->VM options 添加:-Xms258m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m


算子分为两种:
一种称为transformation:转换算子,一般生成的还是一个RDD
转化算子不会立即执行 只有遇到action时才会执行
一种称为action:执行算子,生成的就不是RDD,是一个scala/java对对应的数据结果,数值,集合或者是打印的结果


什么是持久化?
将需要反复使用的RDD,持久化到内存或磁盘中,需要使用时直接调用,不用反复计算


持久化场景?
一个RDD如果被重复使用


持久化的好处?
对于针对一个RDD反复执行多个操作的场景,就只要对RDD计算换一次即可,后面直接使用该RDD,不需要反复计算多次
节省内存资源 提高效率


持久化有哪几种?区别?如何进行持久化?
cache()和persist()两种
区别在于,cache()是persist()的一种简化方式,cache()的底层就是调用的persist()的无参版本,同时就是调用persist(MEMORY_ONLY),将数据持久化到内存中。如果需要从内存中清楚缓存,那么可以使用unpersist()方法。


持久化级别?
在调用persist()是传入对应的StorageLevel
MEMORY :数据持久化到内存
DISK:数据持久化到cp
SER:数据序列化
_2: 复制一份副本


MEMORY_ONLY 以非序列化的Java对象的方式持久化在JVM内存中。如果内存无法完全存储RDD所有的partition,那么那些没有持久化的partition就会在下一次需要使用它的时候,重新被计算。
MEMORY_AND_DISK 同上,但是当某些partition无法存储在内存中时,会持久化到磁盘中。下次需要使用这些partition时,需要从磁盘上读取。
MEMORY_ONLY_SER 同MEMORY_ONLY,但是会使用Java序列化方式,将Java对象序列化后进行持久化。可以减少内存开销,但是需要进行反序列化,因此会加大CPU开销。


MEMORY_AND_DSK_SER 同MEMORY_AND_DSK。但是使用序列化方式持久化Java对象。
DISK_ONLY 使用非序列化Java对象的方式持久化,完全存储到磁盘上。
MEMORY_ONLY_2
MEMORY_AND_DISK_2
如果是尾部加了2的持久化级别,表示会将持久化数据复用一份,保存到其他节点,从而在数据丢失时,不需要再次计算,只需要使用备份数据即可。


Spark提供的多种持久化级别,主要是为了在CPU和内存消耗之间进行取舍。下面是一些通用的持久化级别的选择建议:
优化:就是取舍的思想。取舍资源(cpu + 内存)
一般来说:spark优化的宗旨是速度快优先,前提,spark一般来说,内存是最珍贵的资源,cpu,磁盘其次。


1、优先使用MEMORY_ONLY,如果可以缓存所有数据的话,那么就使用这种策略。因为纯内存速度最快,而且没有序列化,不需要消耗CPU进行反序列化操作。
2、如果MEMORY_ONLY策略,无法存储的下所有数据的话,那么使用MEMORY_ONLY_SER,将数据进行序列化进行存储,纯内存操作还是非常快,只是要消耗CPU进行反序列化。
3、如果需要进行快速的失败恢复,那么就选择带后缀为_2的策略,进行数据的备份,这样在失败时,就不需要重新计算了。SparkStreaming会用到它。
4、能不使用DISK相关的策略,就不用使用,有的时候,从磁盘读取数据,还不如重新计算一次。因为优化的目的就是为了提高速度,有可能持久化到磁盘,再读取数据,这个耗时,可能时间比重新算一遍的时间更长。


宽依赖与窄依赖:
窄依赖是指父RDD的每个分区只被RDD的一个分区所使用,子RDD分区通常对应常数个父RDD分区(O(1),与数据规模无关)
宽依赖是指父RDD的每个分区都可能被多个子RDD分区所使用,子RDD分区通常对应所有的父RDD分区(O(n),与数据规模有关)


相比于宽依赖,窄依赖对优化很有利 ,主要基于以下两点:


1.宽依赖往往对应着shuffle操作,需要在运行过程中将同一个父RDD的分区传入到不同的子RDD分区中,中间可能涉及多个节点之间的数据传输;而窄依赖的每个父RDD的分区只会传入到一个子RDD分区中,通常可以在一个节点内完成转换。


2.当RDD分区丢失时(某个节点故障),spark会对数据进行重算。


1)对于窄依赖,由于父RDD的一个分区只对应一个子RDD分区,这样只需要重算和子RDD分区对应的父RDD分区即可,所以这个重算对数据的利用率是100%的;
2)对于宽依赖,重算的父RDD分区对应多个子RDD分区,这样实际上父RDD 中只有一部分的数据是被用于恢复这个丢失的子RDD分区的,另一部分对应子RDD的其它未丢失分区,这就造成了多余的计算;更一般的,宽依赖中子RDD分区通常来自多个父RDD分区,极端情况下,所有的父RDD分区都要进行重新计算。


调优:
调优前篇:
背景
为什么需要调优??
程序都是能跑的,集群还是那个集群,但是有可能另外一个会调优的人和你写的代码的运行的速度要几倍甚至几十倍


1.开发调优


1.1  原则一:避免创建重复的RDD
 我们有一份数据 ,student.txt
 第一个需求 :wordCount  val stuRDD = sc.textFile("e://sparkData//student.txt")
 第二个需求:算有多少个学生 val stuRDD01 = sc.textFile("e://sparkData//student.txt")
 如果创建两份就会加载两次,浪费性能。但是根据我们的需要来说,同样的算子,需要使用两次,那怎么办?? 
 进行持久化即可:
 sc.textFile("e://sparkData//student.txt").cache()
 


 
1.2 原则二:尽可能复用同一个RDD
这种是大家在开发中,经常写着写着就忘记了
举个例子:
    val namesRDD = starsRDD.map(_._1)
    val name2LengthRDD = namesRDD.map(name => (name,name.length))


    // 这两个map是可以合并的
    val name2LengthRDD01 =  starsRDD.map(tuple => (tuple._1,tuple._1.length))
下面的这种方式写RDD性能更优,因为减少了一次RDD的计算


1.3  原则三:对多次使用的RDD进行持久化


要注意的持久化级别的选择:
1.优先采用MEMORY_ONLY,d但是前提是你的内存足够大,否则可能导致OOM(out of memory 异常)
2.如果MEMORY_ONLY内存不足,就采用MEMORY_ONLY_SER持久化级别,序列化之后,把数据占用的内存变少了,但是序列化和之后使用的反序列化得消耗cpu
3.以上是纯内存持久化,速度很快,但是如果MEMORY_ONLY_SER还是内存不够,那么就采用MEMORY_AND_DISK_SER,采用这种策略,会优先的把数据放在内存中,内存不足放入磁盘
4.不建议使用纯的DISK方案,这样很慢,_2在一些特殊场景(Spark Streaming 容错要求更高)使用以外,一般不建议


1.4 原则四:尽量避免使用shuffle类算子 


对于join,大表join小表,可以考虑把小表的数据广播到executor中,通过map + filter的操作完成join的功能


1.5 原则五:使用map-side预聚合的shuffle操作
定要使用shuffle操作,无法用map类的算子来替代,那么尽量使用可以map-side预聚合的算子。
就是用reduceByKey 代替groupByKey
如果同样的一个需求,用reduceByKey的性能比groupByKey好很多,可以大大减少数据的网络传输


1.6 原则六:使用高性能的算子
有些需求,很多算子都能使用,但是性能不一样,用性能更高算子解决
例如:
使用reduceByKey/aggregateByKey替代groupByKey
使用mapPartitions替代普通map,使用mapPartitions会出现OOM(内存溢出)的问题
使用foreachPartitions替代foreach,类似mapPartitions替代普通map,相比于上面的来说 ,这是一个action算子 ,读写数据库例子
使用filter之后进行coalesce操作
补充:repartition 和 coalesce 的使用场景
repatriation有shuffle,一般是把分区数变多,目的提高并行度  
val rdd02 = rdd01.filter(xxx)  --> 有的分区会过滤很多,有的可能过滤的很少
coalesce 一般来说是把分区数变少,就是把分区数合并,
rdd02.coalesce() ,并行度虽然降低了,但是资源利用率更高,反而可能提高性能


如果需要减少的分区特别少,
rdd01 是20个分区--》rdd02: 5个分区, val rdd02 = rdd01.coalesce(5,true)/rdd01.repartition(5),这样比较好 


1.7 原则七:广播大变量
好处是:
如果使用的外部变量比较大,建议使用Spark的广播功能,对该变量进行广播。广播后的变量,会保证每个Executor的内存中,
只驻留一份变量副本,Executor有多个,而Executor中的task执行时共享该Executor中的那份变量副本。这样的话,
可以大大减少变量副本的数量,从而减少网络传输的性能开销,并减少对Executor内存的占用开销,降低GC的频率。


1.8 原则八:使用Kryo优化序列化性能


做如下配置:


//创建SparkConf对象。


val conf =new SparkConf().setMaster(...).setAppName(...)


//设置序列化器为KryoSerializer。


conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")


//注册要序列化的自定义类型。


conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))


1.9 原则九:优化数据结构


对象,字符串,集合都比较占用内存
字符串代替对象
数组代替集合
使用原始类型(比如Int、Long)替代字符串


使用起来太难,不实用


2.0 资源调优
在executor里面,内存会被分为几个部分:
第一块是让task执行我们自己编写的代码时使用,默认是占Executor总内存的20%;
第二块是让task通过shuffle过程拉取了上一个stage的task的输出后,进行聚合等操作时使用,默认也是占Executor总内存的20%; spark.shuffle.memoryFraction
第三块是让RDD持久化时使用,默认占Executor总内存的60%。spark.storage.memoryFraction 
2.1  


理解 


补充:spark如何配置参数
1.在代码中如何配置参数:
conf.set(key,value)
conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")


对于资源调优,有下面几个参数:
num-executors
作业中,一共给多少个executor
参数调优建议:每个Spark作业的运行一般设置50~100个左右的Executor进程比较合适,设置太少或太多的Executor进程都不好。
设置的太少,无法充分利用集群资源;设置的太多的话,大部分队列可能无法给予充分的资源。


executor-memory  
参数说明:该参数用于设置每个Executor进程的内存。Executor内存的大小,很多时候直接决定了Spark作业的性能,
而且跟常见的JVM OOM异常,也有直接的关联。
640G内存  32g * 20 = 640G


20个executor 
可以看看自己团队的资源队列的最大内存限制是多少,num-executors乘以executor-memory,就代表了你的Spark作业申请到的总内存量(也就是所有Executor进程的内存总和),
这个量是不能超过队列的最大内存量的。此外,如果你是跟团队里其他人共享这个资源队列,那么申请的总内存量最好不要超过资源队列最大总内存的1/3~1/2,避免你自己的Spark作业
占用了队列所有的资源,导致别的同学的作业无法运行。


executor-cores 
每个executor有多少个cpu 核心 
这个核心指的并不是物理核心,指的是逻辑核心 
i7 4核8线程


参数调优建议:Executor的CPU core数量设置为2~4个较为合适。同样得根据不同部门的资源队列来定,可以看看自己的资源队列的最大CPU core限制是多少,再依据设置的Executor数量,
来决定每个Executor进程可以分配到几个CPU core。同样建议,如果是跟他人共享这个队列,那么num-executors * executor-cores不要超过队列总CPU core的1/3~1/2左右比较合适,
也是避免影响其他同学的作业运行。


driver-memory
给dirver程序分配的内存,当有collect操作的时候,需要把dirver的内存给大一点


spark.default.parallelism
参数说明:该参数用于设置每个stage的默认task数量。这个参数极为重要,如果不设置可能会直接影响你的Spark作业性能。


spark.default.parallelism = num-executors * executor-cores (2--3倍)
这样设置之后,那么每个cpu 都是有2-3个task


10个executor  每个executor里面有4个core ,设置spark.default.parallelism = 120
运行executor的core有多少个 40个 ,120/40 = 每个core的task


task的总数,肯定得比分配到cpu core的数量多, 反之浪费资源,一般就是2-3倍比较合适
如何设置并行度:
如何设置一个Spark Application的并行度?


   1.   spark.defalut.parallelism   默认是没有值的,如果设置了值比如说10,是在shuffle的过程才会起作用(val rdd2 = rdd1.reduceByKey(_+_) //rdd2的分区数就是10,rdd1的分区数不受这个参数的影响)


      new SparkConf().set(“spark.defalut.parallelism”,”“500)
  
    2、如果读取的数据在HDFS上,增加block数,默认情况下split与block是一对一的,而split又与RDD中的partition对应,所以增加了block数,也就提高了并行度。
    3、RDD.repartition,给RDD重新设置partition的数量
    4、reduceByKey的算子指定partition的数量
                 val rdd2 = rdd1.reduceByKey(_+_,10)  val rdd3 = rdd2.map.filter.reduceByKey(_+_)
    5、val rdd3 = rdd1.join(rdd2)  rdd3里面partiiton的数量是由父RDD中最多的partition数量来决定,因此使用join算子的时候,增加父RDD中partition的数量。
    6、spark.sql.shuffle.partitions //spark sql中shuffle过程中partitions的数量
 
spark.storage.memoryFraction
用来调节executor中,进行数据持久化所占用的内存大小,默认是0.6


spark.shuffle.memoryFraction
用来调节executor中,进行数据shuffle所占用的内存大小没,默认是0.2




3.spark调优数据倾斜调优


map filter 这种会发生数据倾斜吗??


问题:数据倾斜肯定是某些key发生了数据倾斜,那么如何知道是哪些key倾斜了??
1000万条取2万条出来 
val sampledPairs = pairs.sample(false, 0.1)
val sampledWordCounts = sampledPairs.countByKey()
sampledWordCounts.foreach(println(_))














2.脚本里面可以配置参数
使用格式: 
./bin/spark-submit \
  --class <main-class> \
  --master <master-url> \
  --deploy-mode <deploy-mode> \
  --conf <key>=<value> \
  ... # other options
  <application-jar> \
  
举例:./bin/spark-submit \
  --master yarn-cluster \
  --num-executors 100 \
  --executor-memory 6G \
  --executor-cores 4 \
  --driver-memory 1G \
  --conf spark.default.parallelism=1000 \
  --conf spark.storage.memoryFraction=0.5 \
  --conf spark.shuffle.memoryFraction=0.3 \
  
3.在配置文件中调节参数
 conf/spark-defaults.conf配置文件中读取配置选项。 在conf/spark-defaults.conf配置文件中,
 每行是key-value对,中间可以是用空格进行分割,也可以直接用等号进行分割
 
 问题 :这三个地方都可以配置参数 ,对于同样的一个参数在三个地方都配置了,而且参数的value不一样,那么
 到底是那个生效??
 
 优先级 :
 在代码中的优先级最高 --》 意味一旦写入,在其他地方都不能修改了,除非出现修改代码,打包运行,这样不可取,除非有些参数写入就不修改了,在这里面配置比较合适
 在脚本中优先级其次 --》 非常灵活,一般来说比较适合在这里面写入参数
 在配置文件中最低 --》 以上两种的配置参数,都是针对于该应用的参数,配置文件是全局的参数,优先级最低,更加适合写有些全部都需要用到的参数
 


2.资源调优
3.数据倾斜调优
4.shuffle调优几个部分




调优后篇:
Spark调优由多部分组成,根据不同的业务场景以及数据情况,对Spark作业进行综合性的分析,然后进行多个方面的调节和优化,才能获得最佳性能。
主要分为开发调优、资源调优、数据倾斜调优、shuffle调优几部分;




pip install pywin32-221-cp36-cp36m-win_amd64.whl
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值