一、spark的配置指定
配置文件spark-default.conf
在spark应用内指定或者spark-submit提交应用的命令 --conf指定配置参数
spark-shell编程接口
二、配置的优先级
sparkSession > sparkSubmit > spark-default.conf
三、静态资源配置和动态资源配置
动态资源配置适合一天内作业有高峰期和低谷期的业务。
动态配置:随便写的
spark.dynamicAllocation.enabled true // 默认为false
spark.dynamicAllocation.minExecutors 2
spark.dynamicAllocation.schedulerBacklogTimeout 1m
spark.dynamicAllocation.maxExecutors 20
spark.dynamicAllocation.executorIdleTimeOut 2min
spark在最先启动的时候会申请两个执行器(spark.dynamicAllocation.minExecutors 决定)、
随着任务的积压,每次积压超时时(spark.dynamicAllocation.schedulerBacklogTimeout),驱动器会申请一个新的执行器,以调度任务,直到达到最大的20。如果执行完任务后空闲超过两分钟,驱动器就会关闭这些执行器。
四、spark执行内存和混洗服务
spark.executor.memory 2g 控制执行器的内存
内存组成:运行内存、存储内存、保留内存。
默认先扣除300m的保留内存,剩余60%运行内存,40%存储内存。可以调整每块内存的比例。存储内存没被完全使用时,spark可以将存储内存用作运行内存。
spark.memory.fraction 0.6 运行内存占比
运行内存:进行混洗、排序、链接、集合等操作
存储内存:缓存dataFrame
spark在进行混洗和数据映射时,需要读写大量的本地文件,i/o压力较大,可能会造成性能瓶颈。
i/o参数:
spark.driver.memory 启动器内存大小,接收执行器发送的数据,例如:spark-submit --driver-memory 2g。只有预期驱动器需要回收大量数据(collect操作),或内存不足,可以调整
spark.shuffle.file.buffer: 默认32kb,可以修改1MB,允许spark将映射结果写入硬盘前缓冲多少数据
spark.file.transferTo:默认 true。设为false,会强制spark在最终写入用硬盘前使用文件缓冲来传输文件。会降低i/o压力
spark.shuffle.unsafe.file.output.buffer:默认32KB。混洗操作中合并文件时,缓冲区最大值。对于规模较大的作业,可以设置较大的值1MB,较小的值适合规模小的作业
spark.io.compression.lz4.blockSize:默认32kb,建议提高到512kb,增大压缩数据块的大小,可以减少数据混洗文件大小
spark.shuffle.service.index.cache.size:默认100m。缓存条目受限于以字节为单位的给定内存大小。
spark.shuffle.registration.timeout:默认5000ms,建议提高到120000ms
spark.shuffle.registration.maxAttempts:默认3,可以调大小5
五、 最大化spark的并发度
spark作业包含多阶段,每个阶段包含多个任务,spark会给每个线程分配一个核心,这个线程一次执行一个任务,每个任务处理一个分区
。理想情况下,分区数至少和核心总数一样。
核心:线程:分区= 1:1:1
spark中分区大小:由spark.sql.file.maxPartitionBytes:控制的,默认为128MB。可以减少这个来提高并发度,但是可能会导致小爱哦文件问题。小文件过多,过量的磁盘i/O会导致操作和性能下降。分布式文件系统中,打开、关闭、文件,列目录等文件系统操作,可能因文件过多变慢。
显式调用dataFrame API 的某些方法,也会创建分区。例:读取大文件或创建大型DataFrame时,可以显示制定分区数量
spark.read.textFile(“…/file”).repartition(16)
混洗分区会在混洗阶段创建,默认情况下,混洗分区个数为200。spark.sql.shuffle.partition决定,根据数据集大小更改。
这些分区时需要进行跨网络传输到其它执行器以用与任务对的执行。
spark.sql.shuffle.partition的200默认值,对于小作业来说有点大,需要降低。
六、缓存和持久化cache()、persist()
df.cache() // ds.persist()
df.count()
缓存数据之后,需要将数据进行物化,只有执行了count() 后才算真正的将缓存加载到内存中。take(1)只会缓存一个分区。
cache()
对于cache,尽可能的将所有能读取到的分区都存储到spark执行器的内存中,dataFrame可以被部分缓存,而分区只能被完整的缓存。
8个分区,只能放4.5个,那么只会缓存4个。未缓存的数据需要重新计算
persist()
允许通过StorageLevel控制数据如何缓存。
MEMORY_ONLY:以对象形式存储到内存
MEMORY_ONLY_Y_SER:数据表示为紧凑的字节数组,仅存储到内存。使用时需要反序列化,有一定的性能开销
MEMORY_AND_DISK:直接以对象形式存储到内存,内存不足,会序列化并存储到硬盘。
DISK_ONLY:数据经序列化后,存储到硬盘
OFF_HEAP:存储到堆外内存。
MEMORY_AND_DISK_SER:类似于MEMORY_AND_DISK,但是数据存储在内存也需要序列化(存储到硬盘,总需要序列化)
数据持久化在硬盘,内存中没有,删除缓存:df.unpersist()
可以缓存dataFrame、也可以缓存表和视图,在sparkui可以看到
七、啥时候持久化
1、迭代式机器学习训练中常用的dataFrame
2、执行etl作业或者构建数据流水线,需要对dataFrame进行转化,并且经常访问的dataFrame
八、不适合持久化
1、dataFrame过大,无法放入内存
2、无论dataFrame大小,仅进行开销不大的转化操作时,dataFrame不会被频繁使用。
九、spark的连接算法
内连接、外连接、左连接、右链接。
这些会引发数据在spark执行器间大量移动。
混洗:spark在进行计groupBy、join、agg、sortBy、reduceByKey等,计算要生成的数据时,数据移动的过程。
十、广播hash连接:
又称:映射端链接。是spark最快和最简单的连接。用于一大一小两个数据集,其中一个数据集能够在驱动器内存和执行器内存放的下,大数据集可以避免数据移动。
小数据集通过spark广播变量从spark驱动器分发到所有的执行器。
当数据集小于10MB时,默认使用广播连接。spark.sql.autoBroadcastJoinThreahold 设置,根据执行器和驱动器的内存,可以对大小进行增减。
啥时候用:
1、spark可以将大小两个数据集的每一对等值键都通过hash算法分配到同一个分区
2、一个数据集比另一个数据集小得多
3、进行等值链接,将两个数据集根据匹配的键无序整合起来
4、无须担心将小数据集广播发送到所有spark执行器而引起的网络宽带过多或内存不足
将spark.sql.autoBroadcastJoinThreahold 设为-1 那么,spark始终会选用混洗排序合并连接。
十一、混洗排序合并连接
排序合并算法是根据公共的键,合并两个数据集的高效方法,公共键可能需要排序、不重复、且能发送到同一个分区。
两个数据集的公共键,通过hash算法必须分入同一个分区。
这意味着所有相同键的行,经过hash算法后,必须出现在spark的同一个执行器的同一个分区内。显然执行器之间是需要进行数据交换的,以便将数据放在一起。
分为两个阶段:排序阶段、合并阶段。
默认情况下SortMergeJoin是启用的,控制参数为spark.sql.join.preferSortMergeJoin
如果提前根据键对数据进行分桶和排序,则可以避免spark执行器之间的数据交换(可以避免spark的Exchange 和 sort阶段)。
十二、什么时候使用混洗排序合并连接:
1、可以通过相同的键将大数据集的数据通过hash算法将hash相等的数据分到同一个分区
2、仅基于匹配的排序连接键使用等值连接来整合数据集
3、希望避免exchange和sort操作