本地模式
-standalone
集群模式--yarn ---(client、cluster),区别在于Driver程序在哪里创建
-mesos(了解)
1.mr不擅长迭代式计算(多个mr连续使用,下一个mr基于上一个mr的"落盘"的结果),spark基于内存指的是迭代式计算的中间结果不落盘,shuffle过程还是会落盘
2.RDD->弹性分布式数据集 (是一个数据抽象,底层存储的是逻辑,而非数据,但通过执行代码可以得到数据),通过最后的collect方法可以触发执行
弹性指的是(存储弹性、计算和容错、分区弹性)
分布式:不同分区中的数据会分配给集群中不同的服务器节点计算
每个创建RDD的方法都需源代码中都需要new一个RDD的子类(例如HadoopRDD、parallelizeRDD,同时有RDD的五个特性(getPartitions获取分区
,compute计算每个分区的函数,dependency RDD之间的相互依赖关系,partitionner RDD的分片函数,getPreferredLocations(一个列表,存储存取每个Partition的优先位置)
3.spark 有独立调度器(master-worker...),但主流仍是应用yarn来进行资源调度
4.hadoop里的maptask和reducetask都是进程,而spark改成了线程,所以速度会相对快一些
5.spark/bin 的spark-shell 和 spark-submit比较常用
6.一般mr是需要现在开发工具中写代码->打包->上传服务器->调用jar包,
spark执行有两种方法,1)spark-submit提交jar 2)通过交互式窗口spark-shell,(应用较少,多用于测试)
7.yarn中的nodemanager中的executor是真正干活的线程
8.解决standalone集群执行spark/sbin/start-all.sh时显示java_HOME not set问题:在sbin/spark-config.sh中配置java_home
9. 4040 spark-shell 端口, 18080 spark-jobhistory 端口 ,8080 spark-master端口 (如果zookeeper版本高于3.5,且配置高可用的话,需要将8080修改,一般改为8989)
10.三高特性:高可用,高并发,高流量
11.standalone集群模式有两种运行模式(client、cluster),区别在于Driver程序在哪里创建,若是client
模式,运算结果会显示在提交任务的客户端(worker的executor反向注册),若是cluster模式,需要在服务器上查看结果
12.上传jar包提交spark submit任务时报spark Exception in thread "main" java.lang.RuntimeException: Error in configuring
解决办法:在spark-env.sh中配置如下 export SPARK_DIST_CLASSPATH=$(${HADOOP_HOME}/bin/hadoop classpath)
原因在于Spark编译时都没有将hadoop的classpath编译进去,所以必须在spark-env.sh中指定hadoop中的所有jar包。
13.RDD算子相关的代码在Executor执行,RDD算子外的代码在Driver执行 (包括定义bean对象)
14.RDD算子分为两类,一类是转换算子,转换算子后会创建新的RDD,不会马上执行计算(transformations),另一类是行动算子(actions,例如collect返回结果)
14.获取RDD的方法, 1.from memory sc.parallelize / sc.makeRDD(list) 默认分区数取决于分配给应用的cpu核数 local[] *表示cpu的核数 或者自定义数字
2.from exterior sc.textFile(本地路径/hdfs://IP:端口号/) 默认分区数:math.min(分配给应用的cpu核数,2),或者指定最小分区数
指定分区 >FileInputFormat getSplits 切片 >LineRecordReader next方法 读取
15.
15.1内存创建rdd获取分区内容(可以自定义获取分区数)
def positions(length: Long, numSlices: Int): Iterator[(Int, Int)] = {
(0 until numSlices).iterator.map { i =>
val start = ((i * length) / numSlices).toInt
val end = (((i + 1) * length) / numSlices).toInt
(start, end)
}
}
15.2文件创建RDD
原始数据(22字节)
0 1 2 3 4 5 6 7 8
a b c d e f g X X
9 10 11 12 13 14
h i j k X X
15 16 17 18 19
l m n X X
20 21
o p
切片规划 FileInputFormat ->getSplits
文件创建RDD获取切片大小
return Math.max(minSize, Math.min(goalSize, blockSize)); ->splits.size = 7
//获取分区数
3 = {FileSplit@5245} "file:/I:/IDEAworkspace/spark-demo/input/test.txt:21+1"
2 = {FileSplit@5193} "file:/I:/IDEAworkspace/spark-demo/input/test.txt:14+7"
1 = {FileSplit@5153} "file:/I:/IDEAworkspace/spark-demo/input/test.txt:7+7"
0 = {FileSplit@5125} "file:/I:/IDEAworkspace/spark-demo/input/test.txt:0+7"
最终分区数据
0:abcdefg
1: hijk
2 lmn X X op
3
分区练习1.
原始数据如下,最小分区数为5
0 1 2 3 4
abc
5 6 7 8
ef
9 10 11
g
12 13 14 15
hj
16 17 18
klm
分区数及对应数据(goalsize=3
0: 0~3 abc
1: 3~6 ef
2: 6~9 g
3: 9~12 hj
4: 12~15
5: 15~18 klm
6: 18~19
17.转换算子(Transformation)
单Value
>map :对集合中的元素一个一个算,
>mapPartition是按分区批量计算,
>mappartitionwithindex是将index和分区映射成二元组(index,iter)来计算
>flatMap 对RDD中的元素进行扁平化处理
>glom 将RDD中每一个分区中的单个元素,转化为数组
>groupBy 按照一定的规则,对RDD中的元素进行分组
>filter 按照一定的规则,对RDD中的元素进行过滤
>sample
-参数1:是否抽样放回 true 放回 false 不放回
-参数2: 如果参数1是true,表示期望元素出现的次数 >0
如果参数1是false,表示每一个元素出现的概率 [0,1]
-参数3:随机算法的初始值(种子),一般不指定
-takesample(行动算子)
>distinct
去重,底层是通过 map(key => (key,null)) + reducebykey((a,b) =>a ) 完成去重操作
>改变分区
-coalesce 一般用于缩减分区,默认不执行shuffle过程
-repartition 一般用于扩大分区,默认有shuffle过程,底层调用的是coalesce
>sortBy
按照指定规则,对RDD中的元素进行排序,默认升序
>对于RDD中的每一个分区,都会执行pipe算子中指定的脚本
双Value
>union 合集
>intersection 交集
>substract 差集
>zip 拉链
key-Value模式 :scala中也建议Key采用String或Integer类型
>partitionBy
自定义Partitioner,返回的分区数字不能大于创建RDD时的分区数(类比Mapreduce的numRuducetask与partitionner
numRuducetask大于partition可运行,会导致输出的文件部分为空,
numRuducetask小于partition不可以运行
>reducebykey:按照Key进行聚合,在shuffle之前有combiner(预聚合)操作,返回结果是RDD[K,V]
>groupBykey:按照Key进行分组,直接进行Shuffle
在不影响业务逻辑的前提下,优先选用reduceByKey,求和操作不影响业务逻辑,但求平均值影响业务逻辑
>aggregateByKey(zeroValue)(分区内计算规则,分区间计算规则)
分区内计算参数是定义的初始值与各个key的value值
>foldByKey(zeroValue)(分区内间计算规则)是agregateBykey的简化
>combineByKey(对当前Key的value进行转换,分区内计算规则,分区间计算规则)
*集中聚合算子对比
-reducebykey combineByKeyWithClassTag[V]((v: V) => v, func, func, partitioner)
-aggregateByKey(zeroValue)(cleanedSeqOp,combOp) combineByKeyWithClassTag[U]((v: V) => cleanedSeqOp(createZero(), v), cleanedSeqOp, combOp, partitioner)
-foldByKey combineByKeyWithClassTag[V]((v: V) => cleanedFunc(createZero(), v),cleanedFunc, cleanedFunc, partitioner)
-combineByKey combineByKeyWithClassTag(createCombiner, mergeValue, mergeCombiners)(null)
>sortByKey 按照RDD中的key对元素进行排序
>mapValues 只对RDD中的value进行操作
>join&cogroup cogroup类似全连接,而且在当前的RDD内先进行组合再连接
行动算子(Action)
-行动算子执行后,才会触发计算
>reduce 对RDD中的元素进行聚合
>collect.foreach和foreach
-collect.foreach 将每一个excutor中的数据收集到Driver,行成一个新的数组
.foreach不是一个算子,是集合的方法,是对数组中的元素进行遍历
-对RDD中的元素进行遍历
>count 获取RDD中的个数
>countByKey 获取RDD中每个key对应的元素个数
>first
>take
>takeordered 获取排序后的RDD中的前几个元素
>aggreagate&fold :主要针对单值类型,且初始值是参与分区间计算的
>save相关的算子
-saveAsTextFile
-saveAsObjectFile
-saveAsSequenceFile(只针对kv类型RDD)
18.Spark的Job调度
-集群(Standalone|Yarn)
*一个Spark集群可以同时运行多个Spark应用
-应用(Application)
*一个应用程序(app)可以提交多个job(触发行动算子)
-Job
*Job对应着我们应用中的行动算子,每次执行一个行动算子,都会提交一个Job
*一个Job由多个Stage组成
-Stage
*一个宽依赖转换成一个Stage
*阶段的个数 = 宽依赖个数 + 1
*每个Stage由多个tasks来组成,这些tasks表示每个并行计算,并且会在多个执行器上执行
-Task
*每一个阶段最后一个RDD的分区数,就是当前阶段的Task个数
当启动一个SparkContext的时候,就开启了一个Spark应用,一个驱动程序(driver)被启动了,多个执行器在集群中的工作节点
也被启动了。一个执行器(excutor)就是一个JVM,一个执行器不能跨越多个节点,但是一个节点可以包括多个执行器
19.spark中没有自己的序列化方法,基础数据类型默认是kyro序列化(效率高,但无法控制transit属性),其他类型使用java序列化方法,
实际使用中可以考虑设置kyro序列化来进行优化,(需要重新设置)
// 替换默认的序列化机制
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
// 注册需要使用 kryo 序列化的自定义类
.registerKryoClasses(Array(classOf[Searcher]))
20 * 序列化
* -为什么要序列化
* 因为在Spark程序中,算子相关的操作在Excutor执行,算子之外的算法在Driver中执行,
* 在执行有些算子的时候,需要使用到Driver端定义的数据,这就涉及到了跨进程或跨节点
* 的通讯,所以要求传递给Excutor中的数据所属类型必须实现Serializable接口
* -如何判断是否实现了Serializable接口
* 在作业Job提交之前,其中有一行代码val cleanF = sc.clean(f),用于进行闭包检查,
* 之所以叫闭包检查,是因为在当前函数内部访问了外部函数的变量,属于闭包的形式,
* 如果算子的参数是函数的形式,都会存在这种情况
21. 血缘关系:一般用于数据恢复(容错机制),存在DAG有向无环图
窄依赖(NarrowDependency <- OneToOneDependency,RangeDpendency,PruneDependency) 父RDD中一个分区中的数据,最多交给子RDD一个分区来处理
宽依赖( ShuffleDependency) 父RDD中一个分区中的数据,交给子RDD多个分区来处理 ,往往伴随着shuffle,即会存在缓存,故实际运行中shuffle前的stage可能会跳过
22.JVM分配内存(1/4~1/64)
23.检查点应用场景,血缘关系过长,降低容错,另外为了数据安全,会存储在HDFS上会从血缘关系的最开始执行一遍
24.RDD的持久化
-cache
底层调用的就是persist,默认存储在内存中,应用程序结束后就会清除
-persist
可以通过参数指定存储级别,应用程序结束后就会清除
-checkpoint
*可以当做缓存来理解,存储在HDFS上,更稳定
*为了避免容错执行时间过长,可以使用checkpoint
-缓存不会切断血缘,但是检查点会切断血缘
25.数据的读取和保存
-textFile
-sequenceFile
-ObjectFile
-Json(本质还是通过textFile读取文件,对读取到的内容进行json处理)
-HDFS
-Mysql(重点jdbc)
以元素为单位--以分区为单位
map --- mappartition
foreach ---foreachpartition
26.在Spark里,有三大数据结构
-RDD 弹性分布式数据集
-累加器 分布式共享只写变量
-广播变量 分布式共享只读变量 (可以作为优化选项,减小excutor内存压力,将每个task的变量可以缩减到每个excutor中)
driver="com.mysql.jdbc.Driver"
url = "jdbc:mysql://hadoop102:3306/database"
----------------------------------------------------------------------------------------------------------------------
获取sparksession方式:val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
1.SparkSQL 和 Hive对比
-hive --- 引擎 ---MR
-SparkSQL ---引擎 ---spark
2.SparkSQL对数据集的抽象
-DataFrame 增加了字段内容
*spark.read.不同类型文件
*通过SQL风格操作df 需要创建临时视图
*通过DSL风格操作df,不需要创建临时视图,直接调用
-DataSet 增加了强类型,将java中的类与字段做了映射
3.数据集转换(注意:在spark-shell窗口可以直接调用toDF方法,在IDEA接口中需要import spark.implicits._ )
rdd => DF :var rdd = sc.textFile(path); rdd.map{str => {var fields = str.split(",");People(fields(0),fields(1).toInt)}}.toDF.show(需要提前定义样例类case class People(name:String,age:Int))
rdd.map{str => {var fields = str.split(",");(fields(0),fields(1).toInt)}}.toDF("name","age").show
DF => rdd :var df = spark.read.json(path); df.rdd 得到的RDD[org.apache.spark.sql.Row]
rdd => DS :var rdd = sc.textFile(path); rdd.map{str => {var fields = str.split(",");People(fields(0),fields(1).toInt)}}.toDS.show (需要提前定义样例类case class People(name:String,age:Int))
DS => rdd : var ds = Seq(Person("banzhang",18)).toDS; ds.rdd 得到的RDD[Person]
DF => DS : var df = spark.read.json(path);定义样例类case class People(name:String,age:Int); df.as[People]
DS => DF : var ds = Seq(Person("banzhang",18)).toDS; ds.toDF
Seq(Person("banzhang",18)).toDS.show(较少使用)
4.用户自定义函数
-UDF
一进一出
-UDAF
多进一出
*继承UserDefinedAggreagatedFunction(弱类型)
*继承org.apache.spark.sql.expressions.Aggregator[输入类型,缓存类型,输出类型] (强类型)
-UDTF
一进多出
5.文件加载与数据保存
-spark.read.format("类型").load(path)
-spark.read.json/csv(path)
-df.write.format("类型").save(path)
-df.write.save(path) 默认为snappy压缩,parquet格式
-df.write.mode("append/overwrite/ignore").format("json/parquet..").save(path)
6.使用df创建的临时视图与spark内置的hive没有关系
7.外部hive配置
-确保原有hive是正常工作的
-把hive-site拷贝到spark的conf目录下
-注释hive-site中原有的引擎(tez)
-将mysql连接驱动拷贝到spark的jars目录下
可以用bin/spark-shell页面通过spark.sql("sql") 查询hive表
亦可以用bin/spark-sql页面启动,类似于hive客户端(应用较少)
8.代码中操作hive
-添加依赖<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.1</version>
</dependency>
-拷贝hive-site.xml到resource目录中(需注意target目录下必须也要生成相应文件,否则不能生效)
-虚拟机需要启动hdfs
-注意:创建sparkSession时需要引入该参数启动hive支持 .enableHiveSupport()
9.sparkSQL项目
--1.通过SQL实现求出各个地区热门排名前三名
--1.1从用户行为表中,查询所有点击记录,并和city_info,product_info进行连接
select
c.area,
p.product_name
from
user_visit_action a
left join city_info c on a.`city_id` = c.`city_id`
left join product_info p on a.`click_product_id` = p.`product_id`
where a.click_product_id != -1;
--1.2 按照地区和商品的名称进行分组,统计出每个地区每个商品的总点击量
select
t1.area,
t1.product_name,
count(*) as product_click_count
from
(select
c.area,
p.product_name
from
user_visit_action a
left join city_info c on a.`city_id` = c.`city_id`
left join product_info p on a.`click_product_id` = p.`product_id`
where a.click_product_id != -1)t1
group by t1.area,t1.product_name
--1.3针对每个地区,对商品点击数进行降序排序
select
t2.area,
t2.product_name,
t2.product_click_count,
row_number() over(partition by t2.area order by t2.product_click_count desc) cn
from
(
select
t1.area,
t1.product_name,
count(*) as product_click_count
from
(select
c.area,
p.product_name
from
user_visit_action a
left join city_info c on a.`city_id` = c.`city_id`
left join product_info p on a.`click_product_id` = p.`product_id`
where a.click_product_id != -1)t1
group by t1.area,t1.product_name
)t2
--1.4对查询的结果取前三名
select
*
from
(select
t2.area,
t2.product_name,
t2.product_click_count,
row_number() over(partition by t2.area order by t2.product_click_count desc) cn
from
(
select
t1.area,
t1.product_name,
count(*) as product_click_count
from
(select
c.area,
p.product_name
from
user_visit_action a
left join city_info c on a.`city_id` = c.`city_id`
left join product_info p on a.`click_product_id` = p.`product_id`
where a.click_product_id != -1)t1
group by t1.area,t1.product_name
)t2
)t3
where t3.cn <= 3;
select name,count(*)
from business
where substring(orderdate,1,7) = '2017-04'
group by name;
--2.通过代码实现
--3.城市备注实现(通过自定义UDAF函数)
-------------------------------------------------------------------------------------------
SparkStreaming
-Spark的内置模块,主要用于实时计算
-微批次的流式实时计算框架
-离线和实时
数据处理的延迟
-批和流式
获取StreamingContext方式:val ssc: StreamingContext = new StreamingContext(conf,Seconds(3))
1.批处理间隔是Spark Streaming的核心概念和关键参数,它决定了Spark Streaming提交作业的频率和数据处理的延迟,同时也影响着数据处理的吞吐量和性能。
2.读取数据
ssc.queueStream 从队列获取(一般不用,了解)
ssc.socketTextStream 从端口号获取
ssc.receiverStream 自定义Receiver读取指定数据源
通过读取kafka数据源创建DS
-spark-streaming-kafka-0-8(kafka 0.8 support is deprecated as of Spark 2.3.0)
*receiver方式(过时,从zookeeper中获取offset)
采集器(excutor)从kafka的partition中读取数据,然后分发给excutor进行处理
*Direct方式(从checkpoint中维护offset,应用较少)
Excutor可以直接从kafka的partition中读取数据
>自动维护offset,需要改变Streamingcontext获取方式 StreamingContext.getActiveOrCreate
1.自动维护偏移量,偏移量维护在checkpoint中
2.指定检查点后,还需要设置从检查点中取offset,否则还是会出现丢失数据的情况。
3.修改StreamingContext对象获取方式,先从检查点获取,如果检查点没有,通过函数创建,保证数据不丢失
缺点:
1.小文件过多
2.在checkpoint中,只记录最后offset的时间戳,再次启动程序的时候,会从这个时间到当前时间的所有周期都执行一次
>手动维护offset ,为保证数据的精准一致性,一般维护在有事务的存储上(mysql)
-spark-streaming-kafka-0-10 (主流方式,消费的offset保存在kafka内置topic (consumer_offset)中)
*Direct DStream
*取到的是ConsumerRecords文件,获取值为ConsumerRecords.value
3.架构图
采集线程(ssc.start) ->
Driver -> excutor(处理线程)
4.背压机制
采集线程采集的速度大于excutor处理的速度,可以根据excutor的处理能力动态调节采集速率
5.DStream算子、
-转换算子
*无状态转换
transform
map/flatmap...
*有状态转换
updateStateByKey(Seq,Option) //Seq为相同key的value集合;Option为相同KEY的缓存数据 注:需要指定checkpoint
window
-输出算子
6.累加器和广播变量的使用与RDD相同,但需要注意使用sparkcontext调用累加器
7. sparkcore sparkSQL sparkStreaming
sparkcontext(sc) sparkSeesion(spark) StreamingContext(ssc) 执行入口
rdd dataframe/dataset DStream 数据抽象
8.内核源码分析
-第一部分:将App部署到Yarn服务器
-第二部分:Job以及任务调度过程
App->Job->Stage->Task
-第三部分:提交Task到Executor
-第四部分:Shuffle过程分析
spark优化
RDD中的样例类,可以自动实现序列化
filter一般可以与colesce一起使用进行优化