========================================
MapReduce
分布式计算框架
缺点:
-1.执行速度慢
IO瓶颈:磁盘IO、网络IO
shuffle的机制:数据需要输出到磁盘,而且每次都需要进行排序的操作
-2. 框架的缺陷
只有map和reduce两个操作算子,结构只能是:map -> reduce或者map -> [map ->]* -> reduce [-> map]*
针对比较复杂的任务,需要构建多个job来执行,而当多个job有依赖关系的时候,会增加数据的读写、网络数据传输等方面的问题
-3. Task是以进程的方式来启动,处理小规模数据集的时候速度就会比较慢
Spark:基于内存的分布式计算引擎/框架
起源:加州大学伯克利分校AMPLlib实验室
官网:http://spark.apache.org/
官方博客:https://databricks.com/blog
====================================================
Spark编译
http://spark.apache.org/docs/1.6.1/building-spark.html
源码下载位置:
http://spark.apache.org/downloads.html
http://archive.apache.org/dist/spark/
编译过程:
./make-distribution.sh --tgz \
-Phadoop-2.4 \
-Dhadoop.version=2.5.0-cdh5.3.6 \
-Pyarn \
-Phive -Phive-thriftserver
============================================
Spark的运行模式(Spark应用在哪儿运行)
local: 本地运行
standalone:使用Spark自带一个资源管理框架,将spark应用可以提交到该资源管理框架上运行(分布式的)
yarn:将spark应用提交到yarn上进行运行
mesos:类似yarn的一种资源管理框架
============================================
local
Spark local on linux(spark-shell命令)
Spark Local的环境配置:
-1. 安装好JDK、SCALA、Hadoop等依赖环境
-2. 解压spark编译好的压缩包
cd /opt/cdh-5.3.6
tar -zxvf /opt/tools/workspace/spark/spark-1.6.1-cdh5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6.tgz
-3. 创建软连接
cd /opt/cdh-5.3.6
ln -s /opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/ spark
-4. 进入spark根目录,修改conf中的配置文件
cd spark
mv conf/spark-env.sh.template conf/spark-env.sh
vim spark-env.sh
JAVA_HOME=/opt/modules/java
SCALA_HOME=/opt/modules/scala
HADOOP_CONF_DIR=/opt/cdh-5.3.6/hadoop/etc/hadoop
SPARK_LOCAL_IP=bigdata-01.yushu.com
###
上面四个配置项中,除了HADOOP_CONF_DIR外,其它的必须给定;HADOOP_CONF_DIR给定的主要功能是:给定连接HDFS的相关参数(实际上本地运行的时候,只需要给定core-site.xml和hdfs-site.xml)
###
-5. 测试linux上的本地环境
启动HDFS
./bin/run-example SparkPi 10
./bin/spark-shell
-6. 上传README.md到HDFS根目录
-7. 测试
val textFile = sc.textFile("/README.md")
textFile.count()
textFile.first()
val linesWithSpark = textFile.filter(line => line.contains("Spark"))
linesWithSpark.count()
=======================================
Spark源码导入IDEA
-1. 最好指定maven源为ali的maven源
-2. 将《repository(Windows 开发环境依赖maven本地仓库).zip》解压到本地maven源,默认文件夹位置是:~/.m2
=======================================
WordCount程序
## 1. 读取hdfs上的数据形成RDD
val lines = sc.textFile("/user/yushu/spark/data/word.txt")
## 将RDD的lines对象看成list集合进行操作就可以求解出wordcont的值
lines.filter(_.nonEmpty).flatMap(_.split(" ")).filter(_.trim.nonEmpty).map(word => (word.trim, 1)).groupBy(_._1).map {
case (word, iter) => {
val sum = iter.map(_._2).sum
(word, sum)
}
}.foreach(println)
## 考虑使用RDD的相关API进行WordCount开发
val words = lines.flatMap(line => line.split(" "))
val filteredWrods = words.filter(word => word.trim.nonEmpty)
val wordAndNums = filteredWrods.map(word => (word.trim, 1))
val result = wordAndNums.reduceByKey((a, b) => a + b)
## 结果保存hdfs(要求文件夹不存在)
result.saveAsTextFile("/yushu/spark/core/wordcount/01")
===============================================
## 获取出现次数最多的前10个单词 ===> TopN的几种方式
result.sortBy(t => t._2 * -1).take(10)
result.sortBy(t => t._2, ascending = false).take(10) //降序排列
result.map(_.swap).sortByKey(ascending = false).map(_.swap).take(10)
result.map(_.swap).top(10)
result.top(10)(ord = new scala.math.Ordering[(String,Int)]{
override def compare(x: (String,Int), y: (String,Int)): Int = {
// 如果x>y, 返回1, x = y, 返回0, x < y, 返回-1
x._2.compare(y._2)
}
})
// 获取出现次数最少的前10个单词
result.top(10)(ord = new scala.math.Ordering[(String,Int)]{
override def compare(x: (String,Int), y: (String,Int)): Int = {
// 如果x>y, 返回1, x = y, 返回0, x < y, 返回-1
y._2.compare(x._2)
}
})
====================================================
Spark on Standalone
将spark应用运行在standalone上
Standalone是一个Spark自带的资源管理框架,功能类似yarn
Yarn的框架:
ResourceManager:管理集群的资源,包括:监控、申请...
NondManager: 管理当前节点的资源以及启动container中的task任务
资源:
CPU&内存
Standalone的框架:
Master:集群资源管理,包括:监控、申请...
Worker: 任务执行节点的资源管理,包括资源管理以及executor启动
资源:
CPU&内存
Standalone的配置:
-1. 前提要求:Spark的local本地模式可以成功运行
-2. 修改${SPARK_HOME}/conf中的配置文件
vim spark-env.sh
SPARK_MASTER_IP=bigdata-01.yushu.com
SPARK_MASTER_PORT=7070
SPARK_MASTER_WEBUI_PORT=8080
SPARK_WORKER_CORES=2 ## 一个worker服务中可以包含多少核的CPU,逻辑上的
SPARK_WORKER_MEMORY=2g ## 一个worker服务中可以包含多少内存,逻辑上的
SPARK_WORKER_PORT=7071
SPARK_WORKER_WEBUI_PORT=8081
SPARK_WORKER_INSTANCES=2 ## 指定一台服务器可以启动多少个work服务
mv slaves.template slaves
vim slaves
bigdata-01.yushu.com ## 给定work服务所在机器的IP地址或者主机名,一行一个
-3. 启动服务
./sbin/start-master.sh
./sbin/start-slaves.sh
===> ./sbin/start-all.sh
(关闭服务使用: ./sbin/stop-all.sh)
-4. 测试
jps: 能够看到master和worker服务
webui: http://bigdata-01.yushu.com:8080/
spark-shell测试:
bin/spark-shell --master spark://bigdata-01.yushu.com:7070
val lines = sc.textFile("/user/yushu/spark/data/word.txt")
val words = lines.flatMap(line => line.split(" "))
val filteredWrods = words.filter(word => word.trim.nonEmpty)
val wordAndNums = filteredWrods.map(word => (word.trim, 1))
val result = wordAndNums.reduceByKey((a, b) => a + b)
result.take(10)
=================================================
Spark Standalone分布式配置
-1. 配置hadoop分布式环境前置条件均配置(eg:ssh、ip地址映射...)
-2. 先配置一台成功的单节点的standalone机器
-3. 将所有worker节点的ip地址或者主机名copy到slaves文件中
-4. 将配置好的Spark环境copy到所有的worker服务所在节点上,并且保证在所有的节点上,spark的local模式均可以正常运行
-5. 使用sbin/start-all.sh启动所有服务
=============================================
Spark Standalone Master(HA):
http://spark.apache.org/docs/1.6.1/spark-standalone.html#high-availability
-1. Single-Node Recovery with Local File System
基于本地文件系统的Master恢复机制
修改${SPARK_HOME}/conf/spark-env.sh
SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM -Dspark.deploy.recoveryDirectory=/tmp"
-2. Standby Masters with ZooKeeper
基于zk提供热备(HA)的master机制
修改${SPARK_HOME}/conf/spark-env.sh
SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=bigdata-01.yushu.com:2181,bigdata-01.yushu.com:2181,bigdata-01.yushu.com:2181 -Dspark.deploy.zookeeper.dir=/spark"
=============================================
应用监控
-1. 运维人员会使用专门的运维工具进行监控,eg: zabbix等,(网络、磁盘、内存、cpu、进程等)
-2. 使用CM(CDH)、Ambari(Apache、HDP)大数据专门的运维管理工具
-3. 通过软件自带的web页面进行监控,eg: spark 8080、 hdfs 50070、mapreduce job history 19888....
-4. 通过oozie等调度工具进行监控(当job执行失败的时候可以通知开发人员,通过email、短信)
-5. Linux上的进程好像是可以自动恢复的(supervise方式启动)
===========================================
Spark应用监控:
http://spark.apache.org/docs/1.6.1/monitoring.html
-1. 针对正在运行的应用,可以通过webui来查看,默认端口号是4040,当4040被占用的时候,端口号往上递推.
-2. 针对于已经执行完的job/应用,可以通过spark的job history server服务来查看
MapReduce Job History服务:
-1. 开启日志聚集功能
-2. 给定日志上传的hdfs文件夹
-3. 启动mr的job history服务(读取hdfs上的文件内容,然后进行展示操作)
Spark Job History服务:
-1. 创建HDFS上用于存储spark应用执行日志的文件夹
hdfs dfs -mkdir -p /spark/history
-2. 修改配置文件(开启日志聚集功能)
mv spark-defaults.conf.template spark-defaults.conf
vim spark-defaults.conf
spark.eventLog.enabled true
spark.eventLog.dir hdfs://bigdata-01.yushu.com:8020/spark/history
-3. 测试spark的应用是否会将执行的日志数据上传到HDFS上
./bin/spark-shell
-4. 配置spark的job history相关参数(指定从哪儿读取数据)
vim spark-env.sh
SPARK_HISTORY_OPTS="-Dspark.history.fs.logDirectory=hdfs://bigdata-01.yushu.com:8020/spark/history"
-5. 启动history 服务
sbin/start-history-server.sh
关闭:sbin/stop-history-server.sh
-6. 测试
访问webui: http://bigdata-01.yushu.com:18080/
Spark Job History Rest API:
http://<server-url>:18080/api/v1
eg: http://bigdata-01.yushu.com:18080/api/v1/applicationshttp://bigdata-01.yushu.com:18080/api/v1/applications/local-1498985874725/logs
===================WORDCOUNT案例:=================
def main(args: Array[String]) { val lines=List("hadoop , spark, hbase,hive" , "hadoop,spark,hbase,hive", "", "spark,hive,spark,spark',hdfs,hadoop!") lines .filter(_.nonEmpty) .flatMap(_.split(",")) .filter(_.nonEmpty) .map(word=>(word.trim.replaceAll("!","").replaceAll("'",""),1)) .groupBy(_._1) .map{ case (word,iter)=>{ val sum=iter.map(_._2).sum (word,sum) } } .foreach(println) }===================PV&&UV统计========================
数据格式类似
2013-05-19 13:00:00 http://www.taobao.com/17/?tracker_u=1624169&type=1 B58W48U4WKZCJ5D1T3Z9ZY88RU7QA7B1 http://hao.360.cn/ 1.196.34.243 NULL -1
def main(args: Array[String]) { val conf=new SparkConf() .setMaster("local[*]") .setAppName("pv_uv") val sc=new SparkContext(conf) val time=System.currentTimeMillis() val path="hdfs://bigdata-01.yushu.com:8020/user/yushu/spark/data/page_views.data" val rdd=sc.textFile(path) //公共的计算 val mapredRdd=rdd.map(line=>{ var a=line.split("\t") a }) .filter(arr=> { var flag=arr.length>3&& arr(0).trim.length>10 flag }) .map(arr=>{ (arr(0).trim.substring(0,10),arr(1).trim,arr(2).trim) }) //pv的第一种计算方式 val pvRdd1=mapredRdd .map(t=>(t._1,t._2)) .groupByKey() .map{ case(date,urls)=>{ val pv=urls.size (date,pv) } } pvRdd1.saveAsTextFile(s"/user/yushu/spark/core/pv_uv/${time}/pv/1")//PV的第二种计算方式 val pvRdd2=mapredRdd .map(t=>(t._1,1)) .reduceByKey(_+_) pvRdd2.saveAsTextFile(s"/user/yushu/spark/core/pv_uv/${time}/pv/2") //uv的计算 val uvRdd=mapredRdd .map(t=>(t._1,t._3)) .distinct() .map(t=>(t._1,1)) .reduceByKey(_+_) uvRdd.saveAsTextFile(s"/user/yushu/spark/core/pv_uv/${time}/uv") sc.stop() }===================分组排序TOPN案例========================
数据格式:aa 78 bb 98 aa 80 cc 98 aa 69 cc 87 bb 97 cc 86 aa 97 bb 78 bb 34 cc 85 bb 92 cc 72 bb 32 bb 23
def main(args: Array[String]) { val conf=new SparkConf() .setMaster("local[*]") .setAppName("GroupSortedTopN2") val sc=new SparkContext(conf) val rdd=sc.textFile("datas/groupsort.txt") val result1=rdd.map(line=>{line.split(" ")}) .filter(arr=>arr.length==2) .map(arr=>(arr(0).trim,arr(1).trim.toInt)) .groupByKey() .map(t=>{ val key=t._1 val values=t._2.toList.sorted.takeRight(3).reverse (key,values) }) result1.foreachPartition(iter=>{ iter.foreach(println) }) println("re3------第二种方式:两阶段聚合------") val result3=rdd .map(line=>line.split(" ")) .filter(arr=>arr.length==2) .mapPartitions(iter=>{ val random=Random iter.map(arr=>((random.nextInt(3),arr(0).trim),arr(1).trim.toInt)) }) .groupByKey() .flatMap{ case((_,key),iter) =>{ val r=iter.toList.sorted.takeRight(3) r.map(v=>(key,v)) } } .groupByKey() .map{ case(key,iter)=>{ val r=iter.toList.sorted.takeRight(3).reverse (key,r) } } result3.foreachPartition(iter=>iter.foreach(println)) }================================================
RDD的几个API的应用:
map:数据转换,给定的f返回的数据类型是啥,最终rdd中的数据类型就是啥
flatMap: 数据转换 + 扁平化操作,给定的f返回的集合中的数据类型是啥,最终rdd中的数据类型就是啥
filter:数据过滤,对于给定函数f返回为false的数据进行过滤,保留返回结果为true的数据
reduceByKey: 对于key/value的数据进行按照key进行数据分组,然后对每组数据进行给定函数的聚合操作
====================Linux上执行spark的jar文件================== http://spark.apache.org/docs/1.6.1/submitting-applications.html 通过bin/spark-submit命令进行任务的提交操作 代码打包的jar文件要先上传上Linux上 命令: ./bin/spark-submit \ --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \ --master local[*] \ /home/yushu/logs-analyzer.jar ./bin/spark-submit \ --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \ --master spark://bigdata-01.yushu.com.com:7070 \ /home/yushu/logs-analyzer.jar ./bin/spark-submit \ --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \ --master spark://bigdata-01.yushu.com:7070 \ --deploy-mode cluster \ /home/yushu/logs-analyzer.jar ### 当提交访问为standalone+cluster的时候,最好选择rest url ./bin/spark-submit \ --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \ --master spark://bigdata-01.yushu.com:6066 \ --deploy-mode cluster \ /home/yushu/logs-analyzer.jar ./bin/spark-submit \ --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \ --master spark://bigdata-01.yushu.com:6066 \ --deploy-mode cluster \ --executor-cores 1 \ --total-executor-cores 2 \ --driver-cores 1\ --executor-memory 1g \ --driver-memory 512m \ /home/yushu/logs-analyzer.jar =============================================================
=============================================================
Windows上执行spark的应用
异常信息
-1. Exception in thread "main" org.apache.spark.SparkException: A master URL must be set in your configuration
解决方案:在SparkkConf中指定master的信息
-2. Exception in thread "main" org.apache.spark.SparkException: An application name must be set in your configuration
解决方案:在SparkConf中指定应用名称的信息
-3. null/bin/winutil.exe
原因:windows上没有配置HADOOP环境导致的
解决方案:将hadoop的安装包解药到本机上的任意路径,路径要求不能有空格,不能包含中文;然后将winutils放入hadoop根目录的bin文件夹中,最后配置HADOOP_HOME环境变量
操作完后,重启IDEA,如果重启IDEA无效,重启机器
-4. (windows上可能出现)有可能遇到其它异常,NullPointerException, 这种异常有可能需要修改源码(Hadoop源码)
================================================================
================================================================
Spark应用的参数配置
可以在三个地方进行配置,分别的优先级如下:
1: ${SPARK_HOME}/conf/spark-defaults.conf
2: bin/spark-submit 参数
3:代码中通过SparkConf来给定
优先级:3 > 2 > 1
==================================================================
================================================================== Spark应用的结构 Driver + Executors Driver:运行SparkContext上下文的地方,进行RDD初始化的地方,资源申请、RDD Job调度地方(JVM中) 其实就是main函数运行的地方 Executor:运行具体Task的地方,一个Executor中可以并行的运行多个Task任务 一个Application包含多个Job 一个Job包含多个Stage 一个Stage包含多个Task Task是最小运行单位,是运行在executor中的线程 deploy-mode: 指定driver在哪儿运行 client:默认,driver运行的机器就是执行spark-submit脚本命令的机器 cluster:在集群中任选一台机器作为节点来执行driver进程 ===================Spark的资源调优=========================
主要来讲:调整CPU、内存、executor的数量;通过spark的参数来调整的
http://spark.apache.org/docs/1.6.1/configuration.html
spark-submit的脚步参数资源调优:(相关参数)
--master:给定spark应用运行在哪个地方(eg: local、standalone、yarn、mesos)
--deploy-mode:指定driver在哪儿运行(cluster、client)
--class:指定spark应用的类全称是那个(包名 + 类名),如果该参数不给定,那么应用默认是jar文件中默认的main class
--conf PROP=VALUE: 给定spark应用的参数信息,eg: --conf "spark.ui.port=5050"
--properties-file: 给定应用读取spark的配置信息,默认是conf/spark-defaults.conf
--driver-memory:给定driver的内存,默认1024M
--executor-memory:给定每个executor的内存,默认1024M
--proxy-user:给定运行spark应用的代理用户名称,默认为空
standalone + cluster:
--driver-cores:指定driver的数量,默认1
standalone/mesos + cluster:
--supervise:指定当driver宕机的时候,会自动进行恢复操作
standalone/mesos:
--total-executor-cores NUM: 指定所有的executor的总core的数量,默认情况下是所有core的数量
standalone/yarn:
--executor-cores NUM: 给定每个executor的core的数量,如果是yarn上,默认值为;standalone上默认值为all
yarn:
--driver-cores NUM: 指定cluster的情况下,driver需要的core数量,默认为1
--queue QUEUE_NAME:指定提交到yanr上的时候,提交到那个队列,默认是default
--num-executors NUM:指定executor的数量,默认2个
==========================================================
==========================================================
Spark的内存管理模型(Executor的内存管理)
老模型和新模型比较而言,老模型的资源没有动态分配而已,有些情况下,存在资源利用不充分的问题
spark1.6之前的版本
storage:
指定的是数据缓存所应用到的内存,比如: rdd.cache、rdd.persist、广播变量、共享变量等
由参数:spark.storage.memoryFraction(0.6)控制,默认表示60%的executor的内存用于storage
shuffle:
指定的是在shuffle过程中应用到的内存大小,比如:上一个阶段的数据会先写入内存、shuffle的数据排序等
由参数:spark.shuffle.memoryFraction(0.2)控制,默认表示20%的executor的内存用于shuffle过程
user:
代码中用到的集合、对象等应用到内存,默认占比是(0.2):
1 - spark.storage.memoryFraction - spark.shuffle.memoryFraction
spark1.6+版本
使用老的内存管理模型(1.6之前的):
spark.memory.useLegacyMode:将该参数设置为true即可,默认为false
使用新的内存模型(1.6+)<内存自动分配模型>:
Reserved Memory:
固定内存部分,主要是spark框架使用,默认大小:300M,不可修改
storage&shuffle Memory:
大小由参数:spark.memory.fraction(默认0.75)
由两部分构造:storage memory 和 execution memory(shuffle memory)
动态调整的含义:
当storage部分的内存不够的情况下会自动从execution中获取内存,返之也行
参数:spark.memory.storageFraction(默认0.5),指定storage部分最少内存的占比
动态调整过程:
-a. 如果storage memory满了,execution有多的内存
Storage的操作就会将数据存储到Execution内存部分中
-b. 如果Execution Mmemory满了,同时之前Storage占用了execution部分的内存
将内存中storage部分进行删除,空出内存存储execution部分操作所产生的数据;但是Storage部分内存至少会保留spark.memory.storageFraction给定大小的内存
User Mempry:
用户代码使用到的内存,大小是:1 - spark.memory.fraction(0.25)
eg: 默认情况下一个executor的内存是1024M,使用新的模型来讲:
Reserved:300M
User: (1024 - 300) * (1 - 0.75) = 181M
storage&shuffle:
(1024 - 300) * 0.75 = 534M
最少storage占比:
534 * 0.5 = 267M
====> 动态可分配大小为(534 - 267) = 267M
=======================================================
=======================================================
Spark的动态分配
指的根据业务的需要/应用的情况动态的指定executor的数量
应用场景:streaming或者一致运行的spark程序适应访问高峰和低谷不同情况下的资源利用的问题
相关参数:(一般需要给定四个参数)
http://spark.apache.org/docs/1.6.1/configuration.html#dynamic-allocation
spark.dynamicAllocation.enabled:开启动态资源分配,默认false
spark.dynamicAllocation.initialExecutors:初始化的时候,初始化多少个executor的数量,默认是spark.dynamicAllocation.minExecutors的参数值
spark.dynamicAllocation.minExecutors:给定应用中保持的最小exeuctor的数量,默认0
spark.dynamicAllocation.maxExecutors:给定该应用最多允许持有的executor的数量,默认无,需要给定
====================================================
====================================================
Spark on yarn
http://spark.apache.org/docs/1.6.1/running-on-yarn.html
前提:
1. yarn的配置信息(yarn-site.xml)在spark的classpath中
==> 配置HADOOP_CONF_DIR在spark-env.sh中<配置本地运行环境>
2. 启动yarn
./bin/spark-submit \
--class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
--master yarn \
--deploy-mode cluster \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 1 \
--num-executors 3 \
/home/yushu/logs-analyzer.jar
./bin/spark-submit \
--class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
--master yarn \
--deploy-mode client \
--driver-memory 1g \
--executor-memory 1g \
--executor-cores 1 \
--num-executors 3 \
/home/yushu/logs-analyzer.jar
==============================================================
==============================================================
Spark应用的构建及提交流程:
-1. Driver中构建RDD的DAG图
-2. RDD的job被触发(需要将具体的rdd执行过程提交到executor上执行,此时可以从4040页面看到执行的内容)
-3. Driver中的DAGScheduler将RDD划分为不同的Stage阶段
-4. Driver中的TaskScheduler将一个一个的stage对应的task提交到executor上执行
Spark应用通过spark-submit命令提交的执行过程:
-1. client向资源管理器申请资源(Yarn<ResourceManager>、Standalone<mster>等), 运行driver程序;如果是client模式,driver的运行资源不需要申请
-a. 当运行模式为spark on yarn cluster的情况下,此时driver和ApplicationMaster功能合并
-b. 当运行模式为spark on yarn client的情况下,driver不需要申请资源,此时申请ApplicationMaster运行的资源
driver:DAG的构建、Job的调度
ApplicationMaster: 负责executors的资源申请与管理
-2. 启动Driver(ApplicationMaster)
-3. Driver向资源管理器申请executors的资源
-a. 一台机器上可以运行多个executor ==> 一个NodeManager上可以运行多个executor
-4. executors执行启动
-5. RDD的构建
-6. RDD对应Job的执行
==================================================
==================================================
spark on yarn job history配置
http://spark.apache.org/docs/1.6.1/running-on-yarn.html ===> Debugging your Application
-1. 配置并启动spark的job history相关信息
http://bigdata-01.yushu.com:18080/
-2. 配置yarn-site.xml文件,然后重启yarn
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<name>yarn.log.server.url</name>
<value>http://bigdata-01.yushu.com:19888/jobhistory/job/</value>
</property>
-3. 修改spark-defaults.conf内容
spark.yarn.historyServer.address http://bigdata-01.yushu.com:18080
===============================================================
RDD
Resilient Distributed Datasets ==> 弹性分布式数据集
Resilient:可以存在给定不同数目的分区、数据的缓存的时候可以缓存一部分分区的数据也可以缓存整个rdd的数据、分区的数量可以由业务来定
Distributed:分区的数据可以分布在不同的节点上,并且在不同的executor上执行
Dataset:内部存储的数据类似是一个集合数据
RDD中的数据是不可变的、是分区存在;也就是说每次调用一个api对数据进行操作的时候,实际上是产生一个新的RDD
RDD的底层构建原理:
-1. RDD的分区:
其实是一个逻辑结构,只是一个映射到数据源的结构(怎么读取数据)
分区的数量由:InputFormat的getSplit方法返回集合中的split数量决定,一个split就是一个分区
RDD中其实存储的就是分区的数据,也就是存储的各个分区的位置信息(怎么读数据)
===> RDD中其实是不包含数据的
RDD的构建
-1. RDD的构建底层调用的是MapReduce的InputFormat
-2. sc.textFile模式使用TextInputFormat读取数据
-3. 默认情况下SparkContext中的API,在读取数据的时候使用的是MapReduce的旧API
RDD的创建
-1. 外部数据(非内存数据): 基于MapReduce的InputFormat进行rdd的创建
sc.textFile ==> 底层使用TextInputFormat读取数据形成RDD,内部使用旧MR的API
sc.newAPIHadoopFile ==> 底层使用TextInputFormat读取数据形成RDD,内部使用新的MR的API
sc.newAPIHadoopRDD ==> API明确指定使用哪个InputFormat读取数据
-2. 内存中数据:直接将集合中的数据转换为RDD
val seq = List(1,3,5,6,7,9)
val rdd = sc.parallelize(seq)
RDD的方法类型(API类型)
-1. transformation(transformation算子):转换操作
功能:由一个RDD产生一个新的RDD的API,不会触发job的执行
在这类API调用的过程中,实质是在driver中构建了一个RDD的依赖图,也称为构建RDD的执行逻辑图(DAG图)
是一个lazy的过程
-2. action(action算子):动作/操作
功能:触发rdd的job提交过程,并将rdd对应的job的dag图进行划分后提交到executor上执行
该类型API的调用,会触发job的执行,执行结果会输出到其它文件系统中或者返回给driver中
-3. pesist:(RDD的缓存/RDD的持久化)
功能:将rdd的数据缓存到指定级别的存储系统中,或者将缓存的rdd进行清除缓存操作
rdd.cache() 数据缓存到内存中
rdd.persist(xx) 将rdd的数据缓存到指定级别的存储系统中(内存、磁盘)
rdd.unpersist() 清除该rdd的缓存
注意:rdd的缓存是一个lazy的过程,清空缓存操作是一个立即执行的操作
RDD的输出输出内部其实调用的是:MapReduce的OutputFormat类
=================================================================
=================================================================
Spark优化
-1. 代码优化
-a. 如果一个RDD只使用一次,可以不进行赋值操作,直接调用后续的API ===> 链式编程
-b. 如果一个RDD被多次使用,记住一定要cache
-c. 对于重复操作的API,将其提出并形成一个新的RDD,并将RDD进行缓存
-d. 有限选择reduceByKey和aggregateByKey的API,尽量减少使用groupByKey(groupByKey容易出现OOM和数据倾斜的问题)
-e. 尽量少用可能会产生shuffle的API
-f. 优先使用foreachPartition而不是用foreach API
-2. 资源优化
-3. 数据倾斜优化
导致原因:数据重复分配不均匀导致的,可能会导致某些task执行时间过长或者OOM异常
-a. 更改分区的策略(增加分区的数量 + 根据数据定制开发Partitioner)
-b. 两阶段聚合
===================================================================
===================================================================
RDD依赖
窄依赖
子RDD的每个分区的数据来自常数个父RDD的分区;父RDD的每个分区的数据到子RDD的时候一定在一个分区中
常用方法:map、flatMap、filter、mapPartitions、union、join(要求两个父RDD具有相同的partitioner,同时两个父RDD的分区数量一致,并且子RDD的分区数量和父RDD的分区数量一致)
宽依赖:
子RDD的每个分区的数据来自父RDD的所有分区;父RDD的每个分区的数据到子RDD的时候都有可能分配到任意一个子分区中
常用方法:xxxxByKey、reparation、groupBy、sortBy、join等
==================================================================
==================================================================
Spark应用的结构
Driver + Executors
Driver:运行SparkContext上下文的地方,进行RDD初始化的地方,资源申请、RDD Job调度地方(JVM中)
其实就是main函数运行的地方
Executor:运行具体Task的地方,一个Executor中可以并行的运行多个Task任务
一个Application包含多个Job
一个Job包含多个Stage
一个Stage包含多个Task
Task是最小运行单位,是运行在executor中的线程
Stage的划分规则:
当RDD的DAG图进行提交的时候,会由DAGScheduler对DAG进行stage的划分,划分规则如下:
从后往前进行推断,如果遇到一个宽依赖,就形成一个stage,直到rdd的开头
最后一个Stage叫做ResultStage,其它的Stage叫做ShuffleMapStage
Task:
是Executor中执行的最小单位
task实质上就是分区,一个分区的数据执行代码就是一个Task
分区:从数据的分布情况来讲
task:从数据的执行逻辑情况来讲
一个Stage中的每个Task的执行逻辑(代码)是一样的,只是处理的数据不同而已,task的代码逻辑其实就是对应stage中的API组成的一个执行链
====================================================================
====================================================================
Spark Shuffle机制
Spark的shuffle只存在宽依赖中,也就是说两个stage之间一定存在这儿shuffle过程;stage内部的执行时全部在内存中完成的,不需要考虑写磁盘、不同节点的数据流通
由Spark Shuffle Manager进行管理,参数:spark.shuffle.manager:sort
http://spark.apache.org/docs/1.6.1/configuration.html#shuffle-behavior
Shuffle优化:
-1. spark.shuffle.manager:sort
当task的数量小于200的时候,会自动启用by_pass模式(没有数据排序的操作)
spark.shuffle.sort.bypassMergeThreshold:200
-2. spark.shuffle.manager:hash
当应用中不需要数据排序的情况下,可以考虑使用hash shuffle manager
当分区数量比较多的时候(超过100), 需要开启文件聚合功能:
参数: spark.shuffle.consolidateFiles:默认为false,需要设置为true
==================================================================
==================Spark的调度==================================
Spark Schdeduler
http://spark.apache.org/docs/1.6.1/configuration.html#scheduling
http://spark.apache.org/docs/1.6.1/job-scheduling.html
Spark的Job调度分为FIFO(先进先出, 默认)和FAIR(公平调度)
FIFO: 按照job的提交时间来执行
FAIR:并行的执行job任务
相关参数:
spark.scheduler.mode:FIFO, 给定调度的模式
spark.speculation:是否开启spark的推测执行,默认为false,不开启
spark.task.cpus:1,给定一个task允许需要多少core
spark.task.maxFailures:4,给定一个task最多允许失败次数
==================Spark的优化之两阶段聚合====================================
适用场景:对RDD进行分组操作的时候,某些Task处理数据过多或者产生OOM异常等情况
实现思路:第一阶段给每个key加一个随机数,然后进行局部的聚合操作;第二阶段去除每个key的前缀,然后进行全局的聚合操作
实现原理:将key添加随机前缀的方式可以让一个key变成多个key,可以让原本被一个task处理的数据分布到多个task上去进行局部的聚合,进而解决单个task处理数据太多的问题;随后去掉前缀,进行全局集合,完成功能的实现
优缺点:对于聚合类shuffle操作(groupByKey、reduceByKey等)产生的问题能够很好的解决;但是对于非聚合类shuffle操作( join等)产生的问题很难使用该方式解决
==================================================
对于Driver中的数据需要在Executor中执行的,要求数据必须是可以序列化的,实现了java.io.Serializable;或者使用SerializableWritable将hadoop的writable对象转换为可序列化对象
注意:一般情况下,如果存在调用RDD相关API将executor上的数据获取到driver中,那么要求driver内存是单个executor的1-1.5倍
共享变量
http://spark.apache.org/docs/1.6.1/programming-guide.html#shared-variables
广播变量(broadcast variables)
http://spark.apache.org/docs/1.6.1/programming-guide.html#broadcast-variables
功能:减少driver到executor的数据传输量,将原来每个task拥有一个副本的方式改成一个executor拥有一个数据副本,一个executor中的所有task共享一个数据副本
注意:
1. 广播变量是不允许被修改的,只读的
2. 广播变量在executor中存储在storage内存区
3. 当task执行的过程中发现对应的广播变量不存在了,会自动从driver中重新获取
4. 一般,如果一个广播变量不会使用了,会进行一个清空操作
累加器(Accumulators)
http://spark.apache.org/docs/1.6.1/programming-guide.html#accumulators
功能:类似MapReduce执行过程中的元数据累加操作;累加器在executor中是进行数据累加操作,在driver中进行数据的读取操作(execturo中的数据读取结果不保证<不同executor上结果读取结果不同>)
实现原理:
-1. 每个executor会分别维护一个累加器
-2. 当所有executor执行完成后,所有的累加器会合并形成一个新的累加器
-3. driver中使用的累加器就是最终形成的新累加器
========================广播变量案例=====================
def main(args: Array[String]) { val conf=new SparkConf() .setMaster("local[*]") .setAppName("BVD") val sc=new SparkContext(conf) val rdd = sc.parallelize(Array( "hadoop, yushu' spark! hadoop", "hadoop; spark. spark hbase!!", "hadoop, spark hbase hive?", "hadoop hive spark hive" )) val list = Array(",", "\\.", ";", "!", "\\?", "'") val broadcast=sc.broadcast(list) rdd .flatMap( _.split(" ") .map( w=>broadcast.value.foldLeft(w)( (a,b)=>{ a.replaceAll(b,"")} ) ) ) .map((_,1)) .reduceByKey(_+_) .foreachPartition(iter=>iter.foreach(println)) }======================== SparkCore总结=========================================
Spark的四种运行方式
local
standalone
yarn
mesos
Spark应用的监控
4040 web ui
job history server web ui
job history server rest api
Spark的应用组成结构
Driver + Executors
Application -> Jobs -> Stages -> Tasks(Partition)
Driver的两种运行模式
client
cluster
What is RDD??
RDD的三大类API
RDD的创建、运行以及结果输出的原理/方式 ==>
创建是由InputFormat来做
运行是构建DAG执行图
结果输出是由OutputFormat来做/结果输出可以使用foreachPartition来实现
RDD的依赖 ==> lineage
宽依赖
存在shuffle过程
窄依赖
全部在内存中计算
Spark Shuffle
sort:默认机制,基于数据排序的shuffle
hash:基于数据hash的shuffle
Spark scheduler:
FIFO和FAIR
Spark的内存的管理机制+动态资源分配(分配executor)
Spark Job提交的执行流程(结合wordCount来介绍)
spark的优化:
代码案例:
-1. wordcount
-2. topN
-3. 分组排序TopN(类似MR的二次排序)
额外:
-a. RDD相关API的熟悉