一.spark初始
- 简单介绍
注意有些版本并不是按照顺序来发布的,例如从1.6直接跳到2.0,可以去官网获取最新的版本信息(http://spark.apache.org/)
apache spark 是一个快速的、通用的引擎 用于大规模的数据处理
Apache Spark是一个开源的集群计算系统,旨在使数据分析快速进行
具有快速运行和快速写入的优势
- 伯克利大学数据分析栈
-
一些杂项知识点:
(1) spark的一个计算快优势:基于内存迭代(迭代:上次的计算结果是本次的输入),而MapReduce是基于磁盘迭代。
(2) 开发spark的几种语言:scala(很好)(scala的底层就是java),java(很好),python(不错),R(一般)
(3)spark的源码是用scala语言写的,所以里面的进程都是JVM进程,而用python开发需要python的解释器与JVM进行交互所以能慢点,牵扯到一个效率和兼容性的问题
(4) spark的四种运行模式:
① Local模式:在本地启动多个线程进行模拟执行,多用于测试
② Standalone模式 :standalone是spark自带的资源调度器,类似于MapReduce中的jobTracker和taskTracker
③ Yarn模式:yarn是个资源调度器,将spark放到yarn集群上运行,最具前景
④ Mesos模式:Mesos也是个资源调度器,外国用的较多
类比MapReduce程序:①也有local模式②提交到yarn上去运行③通过eclipse提交应用程序到yarn上去运行 -
开发一个spark应用程序
首先你需要去官网下载对应的jar包导入到项目工程中
放到你eclipse新建项目的lib目录中即可
下面是一个用eclipse编译器,用scala语言编写的 简单的WordCount的spark应用程序
(代码注释是重点)
package com.hpe
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
//http://spark.apache.org 官网地址
object WCSpark {
def main(args: Array[String]): Unit = {
//创建配置对象
val conf = new SparkConf()
//设置App的名称 有啥用? 方便在监控页面找到 例如:MR-》Yarn 8088
conf.setAppName("WCSpark")
//设置Spark的运行模式 local本地运行 用于测试环境
conf.setMaster("local")
//创建Spark上下文 他是通往集群的唯一通道
val sc = new SparkContext(conf)
/**
* 处理数据 在SparkCore中一切得计算都是基于RDD
* R(Resilient)D(Distributed )D(Dataset)
* RDD 弹性分布式数据集
*/
//读取文件内容与RDD形成映射
val lineRDD = sc.textFile("d:/wc.txt")
//基于lineRDD中的数据 进行分词
val wordRDD = lineRDD.flatMap { _.split(" ") }
//每一个单词计数为1 pairRDD K:word V:1
val pairRDD = wordRDD.map { (_,1) }
//相同的单词进行分组,对组内的数据进行累加
//restRDD K:word V:count
//首先会根据key进行分组 例如:(hello,[1,1,1,1,1]),然后将后面这个自定义匿名函数作用于每组数据上,第一次v1就是1,v2是这组数据的第二个1,然后执行后面的逻辑相加,
//将结果赋值给v1,然后v2指针往后移动一位,指向第三个数也是1,以此类推执行完这组数据,将原数据的每一个分好的组返回为(hello,5)这个样式,最后执行完reduceByKey返回为一个RDD
val restRDD = pairRDD.reduceByKey((v1,v2)=>v1+v2)
/**
* 根据单词出现的次数来排序
* sortByKey 根据key来排序
* sortBy 根据自定义的字段来进行排序
*/
// restRDD
// .map(_.swap) //二元组的有个swap方法,实现的功能是使kv交换
// .sortByKey(false) //此时根据key从大到小排序,此时的key是出现的次数了
// .map(_.swap) //排完序把KV再交换回来
// .foreach(println) //action类算子循环打印
//用户可以使用sortBy这个方法,来指定根据哪一个字段来排序
restRDD
.sortBy(x=>x._2, false)//ascending:boolean 是否升序为false代表:从大到小排列,不写默认从小到大排序
.foreach(println)
while(true){}//使程序陷入死循环,不执行完程序或不停止程序,目的是为了用浏览器可以看到该spark应用程序的执行情况,通过localhost:4040端口即可查看
//释放资源
sc.stop()
}
}
本地local模式运行这个WordCount的spark案例,通过浏览器查看处理过程,前提是该程序必须在执行中,如果该程序执行完成,通过浏览器就不能查到,所以在程序中加入了一个死循环目的就是方便通过浏览器查看处理情况的。
关于这张图还存在一个map是否是宽依赖的问题,map发生了shuffle问题,map划分了stage问题:我的猜想是看map的处理逻辑也就自定义的匿名函数来决定的,感觉这才是本质。
二、原理
-
RDD的介绍(Resilient Distributed Dataset)弹性分布式数据集
这里需要注意一个问题:RDD是不存数据的(当然partition也是不存数据的),它存储的是处理数据的逻辑,真正执行的时候是将逻辑发送到数据所在的节点上执行的。
(1)RDD是由一系列partition组成的
(2)RDD提供的每一个函数实际上是作用在每一个partition上的
(3)RDD是有一系列的依赖关系,依赖于其他的RDD
这种依赖关系的作用:提高计算的容错性,如果所需要的RDD处理完的数据损坏,可以去所需要的RDD的父RDD中重新计算数据,得到所需要的数据。
这种依赖关系:子RDD一定知道父RDD是谁,但是父RDD不知道子RDD是谁。
(4)可选项 分区器是作用在KV格式的RDD上
KV格式的RDD:RDD中的数据是二元组类型
分区器的作用:决定数据被哪一个reduce task处理
(5)可选项 RDD会提供一系列的最佳计算位置 -
一些杂项的知识
(1)在spark中没有读文件的方法,依赖的是MR读文件的方法,MR在读文件之前,它会先将文件划分成一个一个的split。
①一个block(块)的大小 ≈ 一个split(切片)的大小
一个block(块)的数量 ≈ 一个split(切片)的数量
约等于的由来:因为block是根据字节来切割的,而task的处理是根据一条一条的记录来处理的,那么肯定会存在该条记录被切碎,分布在两个block上,这时候这个逻辑上的split划分切片的时候会将切碎的那点记录与上一个block块所属的split划分到一块去,所以会出现约等于的情况。
②一个split(切片)的大小 = 一个partition(分区)大小
一个split(切片)的数量 = 一个partition(分区)数量
③一个partition由一个task来处理,计算往数据上移动的。 -
一个spark应用程序在集群中大概运行流程
(1)首先类比一下MapReduce:
MR计算框架(图纸)——》开发的MR应用程序(飞机)——》向资源调度器申请资源(向空管局申请航线)——》任务调度(Driver)——》在集群中就能分布式的并行执行(飞机飞起来了)
Spark计算框架(图纸)——》开发的Spark应用程序(飞机)——》向资源调度器申请资源(向空管局申请航线)——》任务调度器(Driver)——》在集群中就能分布式的并行执行(飞机飞起来了)
对上面的解释:
①对于MR的资源调度器有自带的jobtracker(资源调度+任务调度,导致该节点负载较大,易出现单点故障,不同的集群之间会存在资源抢夺和资源隔离问题,所以出现后面的yarn集群(资源调度(ResourceManager)和任务调度(ApplicationMaster)是分开的))和tasktracker。简单来说现在的MR集群就是yarn集群,已经不用自带的了。
②对于spark应用程序,根据它的运行模式,可以将程序运行到yarn集群上,也可以将程序运行到standalone集群(也叫spark集群,standalone是spark自带的资源调度器)上,当然也有mesos作为任务调度器 -
standalone集群的搭建
(1)关于主从架构:
HDFS ————》NameNode————》DataNode
Yarn—————》ResourceManager–》NodeManager
Standalone—》Master---------------》Woreker
一个节点为什么是主节点,为什么是从节点:原因是在该节点启动了相应的进程!,例如我在node01上启动了一个NameNode进程,所以我说node01为主节点
(2)关于cup,core,thread
一般来讲一个core运行一个thread
但是有些说法例如:4核8线程 代表该cpu支持超线程,如果不支持超线程只能是4核4线程,一旦支持超线程就可以乘2(说明一个core在同一时刻可以处理2个thread,也可以认为你的电脑现在有8个普通的core)。关于这个4核可以是该机器有2个cpu,每个cpu有2核.
讲道理现在一般的服务器都是支持超线程的,你可以先问一下是否支持超线程。
当设置为16,且当前节点的资源够(例如就是8核16线程这样)就代表在该节点可以同时执行16个task(thread)
(3)集群的搭建步骤
conf/spark-env.sh文件中的配置,每个配置项都有对应的英文解释
设置master的IP,
设置master的通信端口
设置每个worker进程管理的cores数
设置每个woerk进程管理的内存数
设置worker进程的工作目录 默认好像是 /temp
设置每个节点创建worker进程的个数,默认是1
bin目录下都是和任务相关的命令(例如提交命令等等)
sbin目录下都是和集群管理相关的命令(例如启动,关闭之类)
这里注意一个问题:这个spark集群的启动命令默认是start-all.sh
,你会发现与同时启动hdfs(单独启动命令是start-hdf.sh
)和yarn(单独启动命令是start-yarn.sh
)命令 重复了(可能还有其余重复的命令),所以如果你要把bin和sbin加入到环境变量path中会出现覆盖问题,你可以选择不加人环境变量,每次找到该目录执行命令,你也可以将该命令更换一个名字。
这个spark-all.sh会调用系统变量$SPARK_HOME,所以这个环境变量是一定要配置正确的
用jps命令在各个节点上可以查询到Master进程和Worker进程
集群成功启动可以通过webUI的监控页面进行查看:
hdfs集群是50070
yarn集群是8088
客户端需要操作高可用的hdfs集群的时候需要连接8020(例如hive连接的时候,eclipse连接的时候都用到了),如果客户端操作的是普通的hdfs集群连接的端口是9000
spark集群是8080,注意与tomcat端口重复了,小心端口占用异常
spark-job的监控端口4040
ss -nal 查看正在监听的端口
提交一个sparkPI测试一下
- SparkPI
================这里需要添加一个导入源码包的操作:关键点是用的是ide,关闭你的项目,来到开始的界面,找到导入项目,选择你的源码包,然后ok,再选择maven项目,一路next,最后它会自动自动联网下载依赖包,需要一定的时间
在file–>Settings可以查看maven的目录以及用的哪一个配置文件,maven的路径等都是可以配置自定义的
π的计算原理图:
int slices = (args.length == 1) ? Integer.parseInt(args[0]) : 2;
slices就是你传入的那个参数,如果没有默认为2
不同的spark版本,代码都不一样了,反正1.6.3和2.3.2的代码都不一样,但是思想是一样的