写点什么

spark 调优(三):持久化减少二次查询

  • 2022 年 7 月 07 日
  • 本文字数:1443 字

    阅读完需:约 5 分钟

大家好,我是怀瑾握瑜,一只大数据萌新,家有两只吞金兽,嘉与嘉,上能 code 下能 teach 的全能奶爸

如果您喜欢我的文章,可以[关注⭐]+[点赞👍]+[评论📃],您的三连是我前进的动力,期待与您共同成长~



1. 起因

在我们接收到数据的时候,通常都需要 etl 处理一下,但原始数据最好也是入库保存一下最好,这样一份数据,我们就使用了 2 次。


Spark 中对于一个 RDD 执行多次算子的默认原理是这样的:每次你对一个 RDD 执行一个算子操作时,都会重新从源头处计算一遍,计算出那个 RDD 来,然后再对这个 RDD 执行你的算子操作。这种方式的性能是很差的。


所以如果你先保存原始数据,再筛选一下的时候,会发现数据会重新被加载,这样是很浪费时间的。

2. 优化开始

对多次使用的 RDD 进行持久化。此时 Spark 就会根据你的持久化策略,将 RDD 中的数据保存到内存或者磁盘中。以后每次对这个 RDD 进行算子操作时,都会直接从内存或磁盘中提取持久化的 RDD 数据,然后执行算子,而不会从源头处重新计算一遍这个 RDD,再执行算子操作。


如果要对一个 RDD 进行持久化,只要对这个 RDD 调用 cache()和 persist()


cache()方法表示:使用非序列化的方式将 RDD 中的数据全部尝试持久化到内存中。


persist()方法表示:手动选择持久化级别,并使用指定的方式进行持久化。


df = sc.sql(sql)df1 = df.persist()df1.createOrReplaceTempView(temp_table_name)subdf = sc.sql(select * from temp_table_name)
复制代码


这种情况就不会重新加载 RDD。


对于 persist()方法而言,我们可以根据不同的业务场景选择不同的持久化级别。



如何选择一种最合适的持久化策略


  • 默认情况下,性能最高的当然是 MEMORY_ONLY,但前提是你的内存必须足够足够大,可以绰绰有余地存放下整个 RDD 的所有数据。因为不进行序列化与反序列化操作,就避免了这部分的性能开销;对这个 RDD 的后续算子操作,都是基于纯内存中的数据的操作,不需要从磁盘文件中读取数据,性能也很高;而且不需要复制一份数据副本,并远程传送到其他节点上。但是这里必须要注意的是,在实际的生产环境中,恐怕能够直接用这种策略的场景还是有限的,如果 RDD 中数据比较多时(比如几十亿),直接用这种持久化级别,会导致 JVM 的 OOM 内存溢出异常。

  • 如果使用 MEMORY_ONLY 级别时发生了内存溢出,那么建议尝试使用 MEMORY_ONLY_SER 级别。该级别会将 RDD 数据序列化后再保存在内存中,此时每个 partition 仅仅是一个字节数组而已,大大减少了对象数量,并降低了内存占用。这种级别比 MEMORY_ONLY 多出来的性能开销,主要就是序列化与反序列化的开销。但是后续算子可以基于纯内存进行操作,因此性能总体还是比较高的。此外,可能发生的问题同上,如果 RDD 中的数据量过多的话,还是可能会导致 OOM 内存溢出的异常。

  • 如果纯内存的级别都无法使用,那么建议使用 MEMORY_AND_DISK_SER 策略,而不是 MEMORY_AND_DISK 策略。因为既然到了这一步,就说明 RDD 的数据量很大,内存无法完全放下。序列化后的数据比较少,可以节省内存和磁盘的空间开销。同时该策略会优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘。

  • 通常不建议使用 DISK_ONLY 和后缀为_2 的级别:因为完全基于磁盘文件进行数据的读写,会导致性能急剧降低,有时还不如重新计算一次所有 RDD。后缀为_2 的级别,必须将所有数据都复制一份副本,并发送到其他节点上,数据复制以及网络传输会导致较大的性能开销,除非是要求作业的高可用性,否则不建议使用。



结束语

如果您喜欢我的文章,可以[关注⭐]+[点赞👍]+[评论📃],您的三连是我前进的动力,期待与您共同成长~

可关注公众号【怀瑾握瑜的嘉与嘉】,获取资源下载方式

发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2022.07.01 加入

还未添加个人简介

评论

发布
暂无评论
spark调优(三):持久化减少二次查询_spark_怀瑾握瑜的嘉与嘉_InfoQ写作社区