Spark原理笔记

分区

1 分区数

默认分区数是分配给运行环境的最大CPU核数
读取外部文件:Math.min(2, 分配给运行环境的最大CPU核数)

2 如何分区

1 数据读取以行的形式读取
2 每行数据读取以偏移量为单位,偏移量不会重复读取
3 读取多个文件时候,计算分区以文件为单位分区

读取外部文件 textFile,我们设置分区数参数时,赋予的值不是实际分区数,而是最小分区数
– 如果路径下所有文件内容总字节数能整除设置的最小分区数,则实际分区数则为设置分区数
– 如果不能整除,看下面例子
注意读取数据时,假设每个分区读取3个字节,读取到某一行时数据超过了3个字节,程序会读完整行数据 (按照 next readLine 方式读取),改分区会记录超过3个字节的整行数据。

设置最小分区为3,外部文件数据如下

例子1
1
2
3
4
1234 数据占用10个字节,每个数字一个字节,每行最后会默认加 /r/n 两个字节,共10个字节。
会先3个分区每个分区写入3个字节,最后一个字节会写入第四个新分区。
虽然设置3个分区,但实际是用了4个分区。

4行对应的字节偏移量范围 分别是
1@@ => 012 [0,3]
2@@ => 345 [3,6]
3@@ => 678 [6,9]
4 => 9 [9,10]
最终输出结果,第一个分区 1,2(0-3之间,按行读会读取完2一整行数据),第二个分区3,第三个分区4,第四个分区空

例子2
123456
123456 占用6个字节。可以被3整除,所以实际分区数为3,每个分区放2个字节

例子3
文件19个字节,设置分区数为5
19/5 = 3,每个分区写入3个字节,所以需要7个分区

**

转换算子

**
懒加载,不会立刻执行

value 类型

map 每个元素一个一个执行
	data = data.map(_*2)
mapPartitions 以分区为单位,一个分区元素执行map
flatMap
	集合List [1,2,3] 扁平为1,2,3。整体转个体
glom
	和flatMap相反,个体转整体
groupBy 分组,每个分区放划分好不同组的数据	
filter 过滤
sample 采样
distinct 去重
	原理:rdd.map(x=> (x,null)).reduceByKey((x,y) => x, numPartitions).map(_._1)
	给每个元素设成 (x,null) 然后聚合在取 第一个字段x值
coalesce 缩小分区,重新分区
	比如去重,过滤之后数据变少,就可以重新分区
	默认分区数据不会打乱重组,不会shffule,只是单纯的合并。可以参数打开重组
repartition 扩大分区,底层是coalesce,默认重新分区,数据会打乱重组
sortBy 默认正序排序
pipe 调用脚本
	针对每个分区,都调用一次shell脚本,返回输出的rdd

双value类型

intersection 交集
union 并集
subtract 差集
zip 集合组合
	分区和分区元素得保持一致

key-value类型

partitionBy 按照K值重新分区
	rdd.partitionBy(new HashPartitioner(2))    这里是hashcode%2取模分区
	
	自定义分区器 rdd.partitionBy(new MyPartitioner(2))
	class MyPartitioner(partitions: Int) extends Partitioner
	重写方法:获取分区个数,和指定key分区规则
reduceByKey 根据相同key聚合
	1 分区内聚合 2 shuffle分区间聚合
groupByKey 按照Key重新分组
    不会分区内处理,直接shuffle分区间分组
aggregateByKey 按照Key处理分区内和分区间的聚合
	aggregateByKey(0)(_+_,_+_) 
    参数: 1 zeroValue 每个分区每个key初始值 0 
    			2 seqOp 分区内计算规则 _+_
    			3 combOp 分区间合并计算规则 _+_
foldByKey 是aggregateByKey简化版本
	分区内和分区间同一计算逻辑,比reduceByKey多一个初始值设置
combineByKey 转换结构后,分区内和分区间的聚合操作
	对拿到的数据做初始化,而不是设置初始值。然后做计算规则
sortByKey 按照key排序
mapValues 只对values操作
join 将相同key对应的多个value关联一起,交叉关联
cogroup 将相同的key,先聚合相同分区对应value,再聚合不同分区对应value

行动算子

RDD

最小的计算单元,RDD数据处理通过装饰者设计模式,逐层套娃,实现最终的计算。
只有执行集合算子才会对数据进行业务逻辑操作。

RDD数据集:

RDD只封装计算逻辑,不存储数据。即下一轮RDD不会存储上一轮RDD的数据

弹性:

内存和磁盘的自动切换,数据丢视的自动恢复,计算出错的重试机制,可根据需要的重新分区

触发任务的执行

collect,take, save

血缘关系

持久化:
	因为RDD不存储数据,通过血缘关系可以恢复数据
	因为RDD不存储数据,如果一个RDD需要重复使用,是需要重头再执行获取数据
	RDD对象可以重用,数据无法重用
	
	对象.cache() 默认临时存储内存中用来重用
	对象.persist(StorageLevel.DISK_ONLY) 
		可修改存储级别到磁盘中 (临时文件,执行完任务会删除)
		涉及磁盘IO,性能会降低
	cache和persist会在血缘关系中添加依赖,数据出现问题可以重头读取
	对象.checkpoint() 
		检查点,需要落盘路径 (执行完任务后不会删除,长久的保存)
		为了保证数据安全,重复使用存储数据需要重头获取。所以为了提高效率,一般和cache联合使用。先cache后checkpoint (cache内存中存储后,就不需要再重头获取数据,减少性能开销)
		执行过程中,会切断并重新建立新的血缘关系 (等同数据源发生改变)
		
依赖
	1 宽窄依赖
	2 toDebugString 查看血缘关系

application - job - stage - task 每层都是一对多

一个applicationContext程序对应一个application
一个行动算子对应一个job
stage 数量 = shuffle/宽依赖 数量 + 1,ResultStage 是最后唯一的执行阶段
task:一个stage阶段中,最后一个RDD分区个数就是task个数

数据结构

累加器
val sum = sc.longAccumulator("sum") 创建 (有log, double, collection类型)
sum.add(数值) 累加
sum.value 获取累加器的数值

广播变量
闭包数据,都以task为单位发送,一个executor包含多个task,多个task内存可能会重复保存数据
广播变量可以将任务数据保存在executor中,达到多个task资源共享executor内存的数据

环境准备 (Yarn)

1 Driver, Executor

yarn cluster 模式
在这里插入图片描述
在这里插入图片描述
yarn client 模式 (简单,略)

组件通信

1 Driver -> Executor
2 Executor -> Driver
3 Executor -> Executor

旧版spark用akka/netty, 3x 版本后完全依赖netty
linux采用epoll形式模仿aio
在这里插入图片描述

应用程序的执行

在这里插入图片描述

1 RDD 依赖
2 阶段的划分
	从后往前包含查找,从resultStage往前包含找stage
	shuffle 数量 + 1
3 任务的切分
	stage最后一轮rdd的分区数量
4 任务的调度
	任务存放在任务池中。默认先进先出调度模式,也可公平调度
	将任务池种取到的任务,序列化之后,发送task到executor (executor选取根据本地化级别来选取)
	
	5种本地化级别
	进程本地化:数据和计算在同一个进程中
	节点本地化:同一个节点中
	机架本地化:同一个机架
	任意本地化
5 任务的执行
6 任务的重试和黑名单
	推测执行:在Spark中,可以通过推测执行,来识别并在其他节点的Executor上重启某些运行缓慢的Task,并行处理同样的数据,谁先完成就用谁的结果,并将另一个未完成的Task Kill掉,从而加快Task处理速度。适用于某些Spark任务中部分Task被hang住或运行缓慢,从而拖慢了整个任务运行速度的场景

Shuffle

在这里插入图片描述

1 shuffle 原理和执行过程
	早期hash shuffle等方式有小文件过多问题。目前shuffle方式通过索引写入一个文件如下图
	shuffle 阶段一定会写磁盘。shuffle落盘数据量减小可以提高性能

在这里插入图片描述

上一轮task数据,先从内存写入file (内存不足时,会有temp临时文件溢写磁盘。溢写文件会归并排序)
下一轮task根据索引来读取节点上文件数据

2 shuffle 写磁盘

如果支持预聚合 (例如map相同key的value相加),也就是其他情况。
则是BaseShuffleHandle处理器,需要进行排序,然后写入同一个文件
预聚合如果内存不足,则会溢写磁盘

如果不支持预聚合,则不排序。为每个分区写一个文件,最后直接合并所有文件回归到一个文件+索引的方式

在这里插入图片描述
3 shuffle 读磁盘
默认48mb缓冲区读取数据

内存的管理

1 内存的分类
2 内存的配置
	spark 3.0之后删除了静态内存管理,只有统一内存管理。包含了动态占用内存机制
	执行内存和存储内存,谁内存富裕可以互相转换
	1 存储内存缺失,执行内存富裕时,存储内存会借用部分执行内存。但是当需要归还给执行存储时,这部分内存执行的数据会被淘汰。如果使用cache方法设定级别是memory-only,是不会溢写磁盘,当这部分内存数据淘汰后,正常情况下也会发生cache数据丢失。
	2 执行内存借用后且仍需使用,存储内存也不足时也无法强制让内存归还,存储内存会溢写磁盘。因为存储内存不足不影响程序执行,最多读取溢写磁盘数据,性能稍微差一点。
	3 存储内存借用后且仍需使用,执行内存也不足时能强制让内存归还。因为如果不归还内存给执行内存,执行内存会执行出错,需要全部重新执行。

在这里插入图片描述

SparkSQL

用来提高开发效率,兼容hive,方便写sql

DataFrame

在这里插入图片描述
以前sparkcore使用的sc.xxx 改成 spark.xxx 使用 DataFrame

val df = spark.read.json("xxxPath.json")
	df.createTempView("user") 创建临时表 /
	df.createOrReplaceTempView("user")  普通表的作用范围是session内, spark.newSession.sql(xxx user) 会找不到报错
	df.createOrReplaceGlobalTempView("user") 是全局表,全局session内都可用
spark.sql("select * from user")

等等大量sql api

DataSet关注类型
DataFrame 是 DataSet 特定泛型,相当于DataSet(row)
RDD和dataFrame之间转换,DataSet和DataFrame转换,DataSet和RDD转换

在这里插入图片描述
可以使用UDF函数,用户自定义函数,代码更简单

SparkStreaming

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱肉肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值