spark 执行过程
yarn 执行过程
*spark的理解
spark是一个快速的、统一的大规模数据处理引擎
它是基于内存计算的
它的特点是:快速、易用、适用于各种数据处理场景(批处理、流处理、交互式处理)、它可以运行在多种分布式计算框架中,如yarn和mesos等
*spark的架构
Master
spark计算集群的主节点,负责接收客户端提交来的spark job,并且负责work节点的资源申请和资源调配,在程序运行时,对各个子节点的状态监控和运行程序的执行情况的查看。
Worker
根据master的资源申请,运行起executor和driver程序,在executor和driver上运行具体的执行程序
spark程序的集群运行方式:
1.把master的url设置成集群的主节点服务的url
val conf = new SparkConf().setAppName("wordcount").setMaster("spark://centos1:7077")
把本地的intellj里的代码打成jar包
然后再sc上添加:
sc.addJar("E:\\bigdata\\sparkfirst20\\out\\artifacts\\sparkfirst20_jar\\sparkfirst20.jar")
然后再intellj上右键运行就能把程序发送到spark集群上来运行
2.把项目打包,然后发送到linux,使用spark的submit指令把程序发布到spark集群上运行
spark-submit
--class com.zhiyou.bd20.WordCount
--master spark://centos1:7077
--executor-memory 1G
sparkfirst20.jar
standalone
client cluster
*spark的编程模型
1.创建sparkconfig对象,用于配置spark运行环境
2.使用config来构建sparkcontext对象,sparkcontext对象主要用于和spark集群服务连接并发布应用程序,同时用于加载数据源和创建rdd、累加器、广播变量
3.使用sparkcontext加载数据源,获取rdd
4.调用rdd的各种transformation方法
5.调用rdd的action方法触发计算job的执行。在action方法中对计算结果进行读值/取值。
6.sparkcontext.stop()
我们开发的spark代码可以分成两块:
1:Driver,application的入口执行程序,一个application的数据处理过程全部都是在driver中来进行定义和配置的。
2:Executor,一个application对数据的处理过程是分布式进行的,分布式处理中的每一个任务执行节点里面会运行起一个executor,executor来接收driver发送过来的任务(task)
driver在运行时,会负责监控每一个executor的执行进度,每一个executor在被分配给driver之后直接向driver负责,接收driver发布进来的指令和任务,在这个过程中,具体task的执行进度状况不再向master汇报。
application,app:一个完整的数据处理过程被称为一个app
一个application程序中包含一个driver和多个executor
Cluster Manager:分布式计算框架的主节点。spark集群来说是:master,对yarn来说是:resource manager
Worker Node:分布式计算框架的子节点。对spark集群来说就是:worker,对yarn来说是:node manager
job:一个application中会有多个job,job是由action方法来触发的,一个action方法就会有一个job。每一个job都会有一个dag图和一个调度,job的执行是由调度按照dag来调配work node子节点上运行其executor来接收并执行task的
Stage:一个job在被解析编译时会划分成多个stage,stage是以shuffle来作为边界划分的。每个stage里有多个task任务。
task:每个job中都会有多个task,task是由driver分配指派给executor来进行执行的
*spark核心api
SparkContext类型
实例化sparkcontext对象:SparkContext.getOrCreate(conf)
创建rdd:
makeRDD:使用driver节点内存中的集合对象创建rdd
parallelize:使用driver节点内存中的集合对象创建rdd
range:获取一个序列rdd
textFile:加载文本格式数据文件获取rdd
sequenceFile:加载sequence个是的数据文件获取rdd
newAPIHadoopFile:用来加载任意格式的mr可以加载的数据文件
newAPIHadoopRDD:用来加载hbase中的数据
创建累加器:
collectionAccumulator
doubleAccumulator
longAccumulator
register
创建广播变量:
broadcast
setCheckpointDir 设置检查点的目录
累加器和广播变量可以优化spark程序
*RDD的api
rdd:弹性分布式数据集。
不可变的、元素被分区的,可被并行操作的数据集
1.rdd是一组分区
2.对rdd调用api方法进行计算时,计算函数是作用于每一个分区的(分布式计算)
3.rdd之间是有依赖关系的,每个rdd都有一个依赖列表,或称为血统关系
4.对于kv的rdd可以指定partitioner对其进行分区
5.rdd在进行加载和计算时会尽可能考虑本地加载和本地计算
窄依赖:父rdd的一个分区的数据进行计算只流向子rdd的一个分区,这种依赖是窄依赖
宽依赖:父rdd的一个分区的数据计算时会流向子rdd的多个分区,这种依赖就叫做宽依赖
有些api方法是窄依赖如:map,flatmap,filter等
有些api方法是宽依赖,如:groupby,reducebykey等
总的来说rdd的方法分为action(返回值类型不是rdd的方法)和transformation(返回值类型是rdd的方法)
rdd本身的api包括:普通rdd(每个元素都是一个对象)和kvrdd(每个元素是一个kv)
kv的rdd除了具有普通rdd的全部功能之外还有额外的功能
数据映射:对数据的转换,解析,一对一计算等功能
map :一个输入元素对应一个输出元素。每一个元素都会经过map的算子被计算
mapPartitions:每一个partition作为一个整体经过mappartition算子被计算(效率更高)
flatMap:一个输入对应多个输出元素
filter:过滤rdd的元素,留下符合条件的数据。每个元素,经过算子输出是true就会被留下,如果是false的话就会被排掉
distinct:排重去重
keyBy:把一个普通的rdd转换成kv rdd的一种方式,它的算子就是确定key值
action:
foreach
foreachPartition
数据聚合的
action:
reduce:不需要初值,算子的输入和输出类型要保持一致
fold:需要初值,算子的输入和输出类型要保持一致
aggregate:需要初值,算子的输入和输出类型可以不一致
max:
min:
count:
countByValue
transformation:
groupBy:对rdd中的数据按照key值进行分组,同一个key在输出rdd里面只存在一次,同一个key下的原rdd的多个value会被合并成一个集合对象Iterable,与key形成一个keyvalue从而形成一个kvrdd。groupby只对数据做一个分组操作,没有进行实际的聚合计算,所以如果需求是做分组并聚合的话尽量避免使用groupby,使用kv rdd的其他更高效的方法来完成分组聚合计算。
排序
action:
first 取出rdd的第一条记录
take 取出rdd中的前N条数据,它是按照rdd的原顺序来取值
takeOrdered 取出rdd按照从小到大的顺序排序后的前N条数据
top 取出rdd按照从大到校的顺序排序后的前N条数据
transformation:
sortBy 是每个分区各自排序
缓存
spark程序的优化手段
当一个rdd会被后续的很多计算进行使用的时候,把这个rdd放在缓存中会提高程序的运行效率
cache:把数据缓存在内存中
persist:接受StorageLevel级别来决定把数据缓存在内存中或者缓存在磁盘上。如果不传参数就是缓存在内存中
unpersist:把rdd从缓存中踢出去
持久化
saveAsTextFile 保存成文本文件
saveAsObjectFile 保存成对象文件
重分区
数据在经过聚合或者过滤等计算之后,原rdd的各个分区中的数据有可能产生倾斜的情况,为了解决数据倾斜的情况,可以对计算后的rdd进行重分区。
coalesce:当原rdd的分区数小于分区后的rdd的分数的时候,可以使用coalesce从而避免分区过程中产生shuffle
repartition:不管重分区的数量怎么发生变化,该方法都是会经过shuffle
集合计算
transformation:
union 并集
++
intersection 交集
subtract 减集
cartesian 笛卡尔乘积
zip 拉链操作
拉链操作要保持对应分区的元素数量要保持一致,不然会报错。
检查点:
checkpoint:
localCheckpoint:
getCheckpointFile
isCheckpointed
collect()
*PairRdd kvrdd
pairrdd具有普通rdd的全部方法和功能。
kvrdd可以由普通的rdd映射而来,映射生成的rdd中的每一个元素是一个元组,这个元组只有两个元素,这样一个rdd就是一个pairrdd
val rdd = sc.parallize(List(1,2,3,4,5,6))
val kvrdd = rdd.map(x=>(x%2,x))
kv的rdd可以由sc加载数据文件而来
映射方法:
mapValues 只对kv中的value进行映射,key保持不变。算子的输入是value,输出是结果的value
flatMapValues
分组聚合函数:
transformation:
下面三个聚合函数的聚合都有两个过程,1:partition内部聚合,2:partition之间的聚合
reduceByKey:根据key值对pairrdd的value进行分组聚合,不需要聚合的初始值,但是要求聚合的结果值必须要和kvrdd的value的类型保持一致。
reducebykey只接受一个算子,这个算子要同时被用于partition内部聚合和partition之间的聚合,所以,这个要求这个算子必须满足两个输入参数的地位要是对等关系。reducebykey值适合进行累加之类的计算
foldByKey:foldbykey除了比reducebykey多了一个初值之外,reducebykey的限制,它一样具有
aggregateByKey:对分组聚合没有太多的限制几乎可以满足所有的聚合
该方法接受三个参数,一个初始值和两个算子,
第一个算子是用于分区内的聚合过程(会用到初始值)
第二个算子是用于分区间的聚合(不会用到初始值)
combineByKey:对分组聚合没有太多的限制几乎可以满足所有的聚合
该方法接受三个参数,三个参数都是算子
第一个算子用于初始值的计算过程.这个算子是会作用于每一个分区的第一个元素。
第二个算子用于分区内的聚合过程(要使用到第一个算子执行结果)
第三个算子是用于分区间的聚合
groupByKey:对数据按照key值进行合并,把一个rdd下某个key值相同的所有的value合并成一个集合对象,并和原key形成一个新的kv
countByKey:统计出相同的key的个数,和原key形成一个新的kv
cogroup:多个kvrdd之间的操作如
kvrddA.cogroup(kvrddB)
先对kvrddA和kvrddB进行groupbykey
keyA:Iterator
keyB:Iterator
join
key,(Iteratora,Iteratorb)
然后对两个groupbykey后的结果进行join(fullOutterJoin)操作,这里的连接是使用的全外连接。
cogroup=groupbykey + fulloutterjoin
groupWith:和cogroup功能用法完全一模一样
数据集操作方法:
subtractByKey 减集
join 内连接 :两个rdd根据key值相互过滤,只有能关联上的数据才会在结果集中
leftOuterJoin 左外连接 :左边的rdd是主rdd右边的是辅rdd,用左边rdd的key去过滤右边rdd的数据,关联上的在结果结果集里面就是Some(辅表的数据),关联不上的就是None
fullOuterJoin 全外链接
rightOuterJoin 右外连接
lookup:查找某个key下的所有value
持久化操作方法:
saveAsNewAPIHadoopDataset:保存数据到hbase的
saveAsNewAPIHadoopFile:以hadoopfile的输出格式保存文件
spark完全支持hadoop的读写
val rdd1 = sc.parallelize(List((1,"张三"),(2,"李四"),(3,"王二")))
rdd1.map(x=>(new IntWritable(x._1),new Text(x._2)))
.saveAsNewAPIHadoopFile("file:///e:/usersparkoutput",classOf[IntWritable],classOf[Text]
,classOf[SequenceFileOutputFormat[IntWritable,Text]])
val rdd2 = sc.newAPIHadoopFile("file:///e:/usersparkoutput",classOf[SequenceFileInputFormat[IntWritable,Text]]
,classOf[IntWritable],classOf[Text])
rdd2.foreach(println)
统计每个用户,login次数,logout次数,view_user次数,new_tweet次数,每个ip上平均行为次数
jim 44 33 22 33 22
每个ip上平均行为次数 = 用户行为次数/该用户使用过的ip数量
维度
指标(KPI)
*共享变量
广播变量:广播变量是在driver上声明,在executor上使用的一些变量数据
广播变量对executor来说是只读的
使用步骤:1.在driver上声明广播变量
2.在executor执行的算子中使用这个广播变量
工作过程中,经常在两张表关联的时候使用广播变量。
spark上在对两个rdd进行关联的时候,如果一个rdd的数据量很小,一个rdd的数据量很大,可以使用广播变量来把关联过程变成map端关联。把小表的数据变成广播变量,广播给每一个加载大表rdd的executor。
累加器:在driver上声明,在executor进行累加奇数,
累加器对executor来说是只写
使用步骤:1.driver上声明累加器
2.在executor上对累加器进行累加数据处理
3.在driver上读取并得到累加结果
当在工作过程中需要一些全局计数类型的聚合的时候,可以考虑使用累加器来替代rddapi方法完成聚合。这种方式可以减少job的数量提高spark的执行效率。
*spark的部署模式
local模式:本地运行模式,一般开发调试时使用该模式。
driver和executor都在本地运行,它是通过多线程来模拟实现分布式的
standalon模式:把spark程序发布到spark的集群上去运行。
client模式:driver运行在客户端,executor运行在集群work节点上
cluster模式:driver和executor都运行在集群的work节点上
cluster模式需要把spark程序的jar包放到hdfs上
spark-submit \
--class com.zhiyou.bd20.WordCount \
--master spark://centos1:7077 \
--deploy-mode cluster \
hdfs://master:9000/path/to/sparkjar
yarn模式:把spark程序发布到yarn集群上去执行。
clinet模式:driver运行在提交运行程序的客户端,executor运行在集群的nodemanager上
spark-submit \
--class com.zhiyou.bd20.WordCount \
--master yarn \
--deploy-mode cluster \
/path/to/jar
cluster模式:driver和executor都运行在集群的nodemanager节点上
*spark程序读写数据库(sql和nosql)
mysql:
读:new JdbcRDD
写:foreachPartition mapPartition
hbase:
读:sc.newAPIHadoopRDD(configuration,classOf[TableInputFormat],classOf[ImmutableBytesWritable],classOf[Result])
写:两种方式:
1.oHBaseRdd.saveAsNewAPIHadoopDataset(job.getConfiguration)。这里要保证rdd的value必须是Put类型或者是Delete类型
2.和mysql一样是用foreachPartition的方式,在算子里面用hbase的api把数据通过hbase服务保存到hbase中
*java开发spark
1.创建项目引入依赖
2.构建sparkcontext对象
3.使用sparkcontext对象加载RDD
4.调用rdd的api方法,传入算子,完成对数据的处理逻辑过程
5.调用rdd的action方法触发计算并读取计算结果
hive
driver
hiveserver2 driver
beeline squrrile
impala cloudera
presto teradata
hive--->hive run on spark
hive可以通过设置让底层执行引擎直接由mapreduce换成spark
shark(sparksql 1.0)
更改了hive的执行引擎为spark,hive核心中的sql优化在自己项目里面以spark为执行引擎的基础上重构
sparksql2.x
*sparksql的开发步骤
1.构建SparkSession
引入sparksession的隐式转换:
import spark.sql
import spark.implicits._
2.用sparksession加载数据源Dataset DataFrame
把dataset或dataframe创建成视图
3.对dataset使用sql来完成数据处理过程
4.对处理后的数据进行读值触发计算过程
5.关闭SparkSession
spark的配置参数:spark.sql.warehouse.dir用来指定给一个目录来作为spark的warehouse目录
*Dataset和DataFrame
DataFrame是Dataset的一个特例
DataFrame = Dataset[Row]
dataframe的row类型可以让我们自定义每一个字段的名称
dataset的字段的名称是自动转换而来
如果每条记录的类型是一个case class的话,那转换成dataset之后,模式字段的名称和类型会和case class的属性的名称和类型保持一致。
数据集每个元素是元组的--------》转成dataframe
数据集每个元素是case class----------》转成dataset
*sparksql数据加载和保存
使用sparksession加载数据
使用dataset的方法保存数据
读:SparkSession.read 使用DataFrameReader的方法加载各种数据源处的数据,可以很方便的加载各种格式的数据文件,和数据库。
写:Dataset.write 使用DataFrameWriter的方法可以方便把数据保存成各种格式和数据存储位置。
select
where
找出personDataSet年龄大于5的数据
personDataSet.createOrReplaceTempView("person")
sql("select * from person where age>5")
-----------------------------
linq写法
personDataSet.select("name","age").where("age>5")
*spark使用hive的metastore和hive进行数据互通
1.hive-site.xml引入项目
2.添加依赖spark-hive
3.在sparksession创建过程中调用builder的enableHiveSupport
如果报元数据版本不一致,把hive-site.xml的参数
hive.metastore.schema.verification=false
*流处理
流处理和批处理的区别
1.流处理对应实时数据处理业务场景,批处理对应历史报表业务场景
2.处理速度上,流处理速度快,批处理速度慢;处理数据量上,流处理处理的数据量小,批处理处理的数据量大
其实对流处理的框架来说处理的数据量的大小,而是使用吞吐量来衡量流处理框架的处理能力
3.批处理程序是使用定时调度来进行运行和终止的。
流处理7X24运行,一旦运行起来,除非更新程序或意外情况,流处理不会被终止。
4.批处理启动后只执行一次计算
流处理启动后可能会执行无数次计算
在数据处理上
流处理有时间的概念
批处理只有时间点的概念
*spark streaming的开发流程
1.构建StreamingContext,这个类型封装了SparkContext
2.从数据源处加载流数据,获取DStream,它封装了rdd
3.调用DStream的各种api方法完成流处理过程
4.启动流计算
*spark streaming开发重要内容
1.获取数据源(kafka flume socket mq)
2.DStream的api方法
*时间间隔长度
Duration、Minutes、Seconds
需求:
实时展现商品销量的top5
1.流数据源(nc -lk)
订单id 产品id 订单产品数量 订单金额
1 2 3 344
1 1 1 111
1 1 1 111
1 2 3 123
2.统计出每个产品的总销量和每个产品的总订单金额
transformation:
map算子
reducebykey算子
action:
foreachpartition算子
3.取出销量的top5
mysql数据库设计
create table order_stastic(
product_id integer,
account_num integer, -- 累计销量
amount_num integer, -- 累计总金额
primary key(product_id)
)
数据每个微批次都更新到mysql的这张表
使用:
实时查看销量top5:
select * from order_stastic order by account_num desc limit 5
实时查看销售金额top5:
select * from order_stastic order by amount_num desc limit 5
作业
计算累计的wordcount
计算结果保存在hbase中
*dstream的api
映射:
transformation:
flatMap
map
mapPartitions
filter
action:
foreachRDD
print 打印前10条数据
聚合函数:
reduce
count
countByValue
-------------------------------------------------
缓存:
cache
persist
重分区:
repartition
持久化:
saveAsTextFiles
*kvdstream(pairdstrea-m)的api
一个dstream只要符合如下条件就自动具有paridstream的所有方法:
dstream中每个元素是一个元组,每个元组中有两个元素
pairdstream具有dstream的所有方法和功能
映射
mapValues
flatMapValues
聚合:
分组聚合
reduceByKey
combineByKey
groupByKey
cogroup,先groupby 再join
持久化:
saveAsNewAPIHadoopFiles
数据集合数据集的关联操作:
join
leftOuterJoin
rightOuterJoin
fullOuterJoin
*dstream附加算子
统计近10分钟之内接受到的数据的wordcount累计值
窗口计算算子
1.窗口的宽度和微批次时间长度一样是个时间长度的概念,一个窗口包括若干个微批次时间长度
2.窗口宽度必须是微批次时间长度的整数倍
3.当计算的时间间隔需要超过微批次的时间间隔的时候,可以使用窗口滑动参数来进行设置计算的时间间隔。窗口滑动的宽度也必须是微批次时间长度的整数倍。(当窗口计算没有滑动参数的时候,计算的时间间隔是微批次的时间,当窗口计算有滑动参数的时候,计算的时间间隔就是滑动参数的时间长度)
groupByKeyAndWindow
reduceByKeyAndWindow
countByValueAndWindow
countByWindow
reduceByWindow
统计近1分钟个之内的累计wordcount
window:开窗算子
累计计算算子:
这种累计计算的算子只有在pairdstream里面才有,普通的dstream不具有累积计算的功能。
mapWithState
updateStateByKey