大数据(big data),IT行业术语,是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。
总而言之就是原先一台电脑无法处理的文件(因为数据量太大了),这时候我多加几台机器,原先一台电脑的算力无法计算数据,现在我加几台机器同时计算这份数据,是不是就能把这份数据给处理了,换个白话文,假如4G内存
无法算清8G
数据,这时候我用两台4G内存
的计算机是不是就可以算完8g
数据(mapreduce
),再或者我有16G
的数据 ,物理磁盘只有4G
这时候我是不是可以用4台计算机平摊存储这16G
的数据(hdfs
),每个机器的4g
内存计算 4g
物理数据,我是不是可以让这台机器先算2G
数据 再算2G
数据,但是我又想人为的控制可能不太友好,所有我用了yarn
,让YARN
对我的数据计算进行合理分配资源
Spark是一种主要基于内存的计算的并行计算框架,提到Spark我们不得不想到另外的计算框架:MapReduce
,Spark
的出现解决了MapReduce
很多痛点(不知道为啥所有组件都喜欢和MapReduce对比
)
MapReduce
API
不够灵活,只能在map
方法和reduce
自己实现逻辑Shuffle
,频繁读写HDFS
,效率低Spark
MapReduce
快几倍甚至100倍Api
,支持多种编程语言(java
, scala
, python
)
Hdfs
Hbase
Es
和Mongodb
等StandAlone
模式: 自带的集群模式Yarn
: 通用调度框架Local模式
:方便开发调试RDD
、DataSet
、DataFrame
、DStream
)Shuffle
时数据落本地磁盘,执行checkpoint时会将本地磁盘数据上传至hdfs
目录cache
和checkpoint
保证安全Applicatition(应用程序):
指用户编写的SPARK
应用程序,包含驱动程序(Dirver
) 和 之后会分布在多个节点上运行的Executor
代码
Dirver(驱动程序):
运行Application
中的main
函数,并通过new sparkContext()
代码,为Spark
程序生成运行环境,主要通过Master
进行资源的申请,任务的分配和监控等,当Executor
运行完毕后,Driver
负责将SparkContext
关闭,通常用SparkContext
代表Driver
Master(总控进程):
Spark Standalone运行模式下的主节点,负责管理和分配集群资源来启动Spark Application
Worker(工作节点):
集群中任何可以运行Application
代码的节点,standalone 模式下为slaves
配置的节点名称
Executor(执行进程)
Application
运行在Worker
节点上的一个进程,该进程负责运行Task
,并负责将数据存在内存或者磁盘上。
partition(分区数)
:
Task(计算任务)
: Spark
程序切分的最小单位,负责执行Executor
分配的任务,内部运行RDD
任务集,每个分区执行一个task
任务
RDD
就需要5轮次object WordCount {
def main(args: Array[String]): Unit = {
//创建SparkContext
val conf = new SparkConf().setAppName("WordCount")
//SparkContext是用来创建最原始的RDD的
val sc: SparkContext = new SparkContext(conf)
//创建RDD(Lazy)
val lines: RDD[String] = sc.textFile(args(0))
lines.partitions
//Transformation 开始(Lazy)
//切分压平
val words: RDD[String] = lines.flatMap(_.split(" "))
//将单词和一组合
val wordAndOne: RDD[(String, Int)] = words.map((_, 1))
//分组聚合
val reduced: RDD[(String, Int)] = wordAndOne.reduceByKey(_ + _)
//排序
val sorted: RDD[(String, Int)] = reduced.sortBy(_._2, false)
//Transformation 结束
//Action算子,会触发任务执行
//将数据保存到HDFS
sorted.saveAsTextFile(args(1))
//释放资源
sc.stop()
}
}
# 执行任务
spark-sbumint xxx.jar xxx.WordCount
等同于
/opt/module/jdk1.8.0_144 -cp \
/opt/module/spark-2.1.1-bin-hadoop2.7/conf/:/opt/module/spark-2.1.1-bin-hadoop2.7/jars/*:/opt/module/hadoop-2.7.2/etc/hadoop/ \
-Xmx1g -XX:MaxPermSize=256m \
org.apache.spark.deploy.SparkSubmit \
1.在启动Spark时,spark会先帮我们把Spark的Master,Worker启动起来,Worker向Master上报当前信息(计算机ip及名称,核数,物理磁盘大小,内存大小等信息),由Master收集为一张表,同时Master与Worker搭建心跳桥梁,由Worker上报心跳状况至Master,Master通过各自的RpcEnvPoint进行通信,Worker会启动Executor进程执行task任务,通过application生成执行计划,划分state,通过state与state的依赖关系,划分分区任务,分区个数为task任务个数,当Worker与Master 失败达到一定次数时汇报Worker节点宕机
2.在Spark-submit提交程序命令时,假设当前为client(客户端提交模式),会在本地启动Driver进程,通过SparkConf收集spark资源配置信息,通过ConcurrentHashMap进行收集
3.当执行到SparkContext时,会向Master发送注册Application的信息,此时Driver才算是与Master进行连接成功,运行接下来的程序时,Dirver会将RDD数据集提交至Master,由Master分配Worker去执行任务
4.通过ReadTextFile从本地读取文件,此时partition就是默认设置的partition数量,在默认partition的算法中,默认初始RDD的最小partition的大小只能为1或2
(sc.defaultMinPartitions=min(sc.defaultParallelism,2))
5.因为我没设置分区数,所以默认分区数为cpu核数,但是与2取最小分区数量,所以为2
6.flatMap,map底层都没有runJob提交命令,所以不会触发shuflle,因此words,wordAndOne 的分区数量都为2
7. 但是当遇到reduceByKey(_ + _) 算子是,因为底层触发了 shuffle算子(new ShuffleRDD()),所以数据会被重写洗牌,同时写入本地磁盘(生成index和data文件),分区数量进行改变,会变成了defaultParallelism(cpu)核数的分区大小 >2
8. 因为sortby也会触发存在subJob方法,所以SortBy也会触发Shuffle触发,所以此时分区大小 与defaultParallelim个数一致
Dataset
和DataFrame
DataSet
对比DataFrame
,多了类型指定,方法更加健壮,丰富,可以同时使用RDD
算子方法和DataSet Sql
方法,而DataSet
只能使用SparkSql
单一操作,通过引入import
spark.implicits._
依赖 调用.DF
可直接使用,DataFrame = DataSet[Row]
通常是为了实现mapside join
,可以将Driver端的数据广播到属于该application的Executor,然后通过Driver广播变量返回的引用,获取实现广播到Executor的数据
累加器相当于分布式中统筹变量,分布式累加,在Driver端定义初始化,在Executor端累加
转换算子: 转换算子不触发提交作业,完成作业中间处理过程,懒加载算子,需要行动算子操作的时候才会触发运算
行动算子:这类算子会触发SparkContext
提交job
作业,行动算子和转换算子,再源码中多了一个runJob() 方法调用
持久化算子
cache: 将经过某个算子计算后的结果全部装载进入内存,加快后续重复使用效率
persist:可指定存储级别,默认是物理存储,可以选择内存或者物理存储级别
checkpoint: 转化算子,遇到行动算子后,会单独开启一个新的job做checkpoint,这时候会切断血缘关系
map
和mappartition
map
是针对RDD
中的每一个元素进行操作mapPartition
是对RDD
的每一个分区的迭代器进行操作,返回的是迭代器,迭代器存的是地址信息理解思路,假设读取spark
读取hdfs
文件,按照split
切割,原有200mb
的文件,按照128mb
切割为一个分区,所以此时的分区数为2,假设要对每行数据的后缀加上.index
后缀,使用map
,会对每行数据进行操作,效率十分缓慢,有多少个元素就会执行多少次,而使用mappartition
操作时,是分区级别操作,减少大量开关操作,且在一个并行度中操作所有元素,有多少个分区就会执行多少次,此次操作为2次,独立在每个分区上运行,所以mappartition
效率会比map
高很多,但是mappartition
并不代表一定,假如一个partition有很多数据的话,一次函数处理可能会导致oom
,普通的map一般会导致oom
MapPartitionsDemo.scala
sortby
和sortbykey
sortby底层参考的是sortbykey,对数据进行keyby,之后进行排序
groupByKey
和reduceByKey
groupbykey
和reducebykey
在代码中的表现就是是否开启了预聚合模式
cache
和persist
cache
的底层是调用persist(磁盘)
的Memsist(内存)
级别
广播变量一但广播出去就不能改变,为了以后可以定期的改变要关联的数据,可以定义一个object[单例对象] (全局变量),在函数内使用,并且加一个定时器,然后定期更新数据,不使用广播变量解决问题
广播遍历并不是存储再Driver,而是在每个executor
中都存储一份,广播遍历块(broadcast-black
) 每个Executor
最高只能存储40Mb
的数据,在查询广播时,会先从本地的broadcast-black
中先进行查询,当自己本地没有时,会向其他Executor
进行请求查询,消耗网络资源
topN
的最优方案先分组,toList
然后在内存中排序,每个组中的数据比较大,可能会产生内存溢出
自定义分区器,然后在分区内排序,可以使用TreeSet
, 算子 top
的源码就是如此
使用方法:
it.foreach(t => {
//将数据添加到treeset中
sorter += t
// 删除树形结构的 最后一个数据
if (sorter.size > topN) {
sorter -= sorter.last
}
})
为什么使用TreeSet
而不推荐使用ArrayList
在时间复杂度中,顺序结构 和 链表结构的时间复杂度不同,顺序表的时间复杂度为 O(n^2) 而二叉数属于链表结构 他的时间复杂度为
log2^n
所以推荐使用TreeSet
1.检查是否开启spark executor的动态感知策略,spark是默认开启动态感知策略,这个时候是需要关闭的,例如我生成申请 20个executor,但是实际只产生了 11个Executor,这个时候就是出现了开启动态策略的故障
写入关系型数据库:在Driver端获取偏移量,然后将计算好的结果和偏移量,使用支持事务的数据库,在同一事务中将偏移量和计算结果更新到数据库中
写入非关系型数据库中,将偏移量和计算好的结果同时写入到Hbase或ES的同一行中
Spark
的分区数和Kafka
的分区数需要一致,才能达到两个组件的最大吞吐量,过多的消费者只会造成资源浪费,同时过少的消费者会消耗资源,原先一个spark task只消费一个分区数据,现在一个spark task消费两个分区数据
介绍:
我们平时写Spark Job的时候最长苦恼的应该就是如果和调节memoery,vcore这些参数,资源申请少了会造成job的失败,多了就会造成资源的浪费。所以Sparklens就是这么一个让你更加了解你的job运行情况,从而有效的进行Spark 优化的工具。
问题产生原因:
--packages qubole:Sparklens:0.3.1-s_2.11
--conf spark.extraListeners=com.qubole.Sparklens.QuboleJobListener
原在线调用sparklens
方法,该方法一执行就会从网上拉取sparklens
包进行解析执行,但是http://dl.bintray.com/spark-packages/maven/
不知怎么,网站出现问题无法下载sparklens
包,–packages命令我们就没有办法使用,所以我们更改为以下方法
1.准备工作下载`spark lens.jar`包
https://mvnrepository.com/artifact/qubole/sparklens/0.3.2-s_2.11
2.将下载好的 0.3.2-s_2.11.jar 放入lib下,添加如下参数(此路径随意)
--jars ./lib/sparklens-0.3.2-s_2.11.jar
--conf spark.extraListeners=com.qubole.sparklens.QuboleJobListener
--conf spark.sparklens.data.dir=/tmp/spark/sparklens # 默认保存hdfs上
--conf spark.sparklens.report.email=<email> # 可以将运行结果发送至邮件中
样例:
${SPARK_HOME}/bin/spark-submit \
--master yarn \
--deploy-mode client \
--class com.meat.main.ApplogODS2DWD \
--driver-memory 512M \
--executor-memory 512M \
--executor-cores 1 \
--queue default \
--num-executors 3 \
--jars ./lib/sparklens-0.3.2-s_2.11.jar \
--conf spark.extraListeners=com.qubole.sparklens.QuboleJobListener \
/root/project/bigdata-dw-spark-offline/bigdata-dw-spark-offline.jar yarn ${dt}
在程序运行结束时会出现sparklens分析结果
3.如果没有及时保存sparklens分析结果,在·spark.sparklens.data.dir· 下可找到刚分析的json离线文件
找到spark-submit单独执行
./bin/spark-submit --jars ./lib/sparklens-0.3.2-s_2.11.jar --class com.qubole.sparklens.app.ReporterApp qubole-dummy-arg <filename.json.path> 即可再次得到分析结果
将Hive风格的Coalesce and Repartition Hint 应用到Spark SQL需要注意这种方式对Spark的版本有要求,建议在Spark2.4.X及以上版本使用,示例:
INSERT ... SELECT /*+ COALESCE(numPartitions) */ ...
INSERT ... SELECT /*+ REPARTITION(numPartitions) */ ...
receiver 可以简称为高级API,receiver模式是借助外界的东西,比如zookeeper来维护消费者偏移量,并且是master节点接收到消息后,首先发送给从节点做zookeeper备份,然后再发送到driver端去执行
direct模式是kafka自己去维护偏移量的,kafka充当储存数据的乙方,sparkStreaming是主动去kafka中拿数据的,不需要一个task一直被占用接收数据,基于direct模式的offset是存储再内存中的
kafka的偏移量是单独存在一个_consumer_offsets主题内的
transient
修饰,变量就将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问transient
关键字只能修饰变量,而不能修饰方法和类,注意,本地变量是不能被transient关键字修饰的,变量如果是用户自定义类变量,则该类需要实现Serializable
接口transient
关键字修饰的变量不能再被序列化,一个静态变量不管是否被transient
修饰,均不能被序列化