《Spark快速大数据分析》
8.4.2 关键性能考量 内存管理
内存对Spark来说哟几个不同的用途,理解并调优Spark的内存使用方法
可以帮助优化Spark应用。在各个执行器进程中,内存有一下所列集中用途。
RDD存储 |
当调用RDD的persist()或cache()方法时,这个RDD的分区会被存储到缓存区中。 Spark会根据spark.stroage.memoryFraction限制用来缓存的内存占整个JVM堆空间的比例大小。 如果超出限制,旧的分区数据会被移出内存。 |
数据混洗与聚合的缓存区 |
当进行数据混洗操作时,Spark会创建出一些中间缓存区来存储数据混洗的输出数据。 这些缓存区用来存储聚合操作的中间结果,以及数据混洗操作中直接输出的部分缓存数据。 Spark会尝试根据spark.shuffle.memoryFraction限定这种缓存区内存占总内存的比例。 |
用户代码 |
Spark可以执行任意的用户代码,所以用户的函数可以自行申请大量内存。 例如,一个用户应用分配了巨大的数组或者其他对象,那这些都会占用总的内存。 用户代码可以访问JVM堆空间中分配给RDD存储和数据混洗存储以为的全部剩余空间。 |
默认情况下Sparkhi使用60%的空间来存储RDD,20%存储数据混洗操作产生的数据,
剩下的20%留给用户程序。用户可以自行调节这些选项来追求更好的性能表现。如果用户代码
上分配了大量的对象,那么降低RDD存储和数据混洗存储所占用的空间可以有效避免内存不足的情况。
除了调整内存各区域比例,还可以为一些工作负载改进缓存行为的某些要素。Spark默认的cache()操作
以MEMORY_ONLY的存储等级持久化数据。这意味着如果缓存新的RDD分区时空间不够,旧的分区就会直接被删除。
当用到这些分区数据时,再进行重新计算。所以有时以MONORY_AND_DISK的存储等级调用persist()方法会获得更好的
效果,因为i在这种存储等级下,内存中放不下的旧的分区会被写入磁盘,当再次需要用到的时候再从磁盘上
读取回来。这样的代价有可能比重新计算各分区要低很多,也可以带来更稳定的性能表现。当RDD分区的重算代价很大时,
这种设置尤其有用。
对于默认缓存策略的另一个改进是缓存序列化后的对象而非直接缓存。可以通过MEMORY_ONLY_SER 或者 MEMORY_AND_DISK_SER
的存储等级来实现这一点。缓存序列化后的对象会使缓存过程变慢,因为序列化对象也会消耗一些代价,
不过这可以显著减少JVM的垃圾回收时间,因为很多独立的记录现在可以作为单个序列化的缓存而存储。
垃圾回收的代价与堆里的对象数目相关,而不是和数据的字节数相关。这种缓存方式会把大量的对象
序列化为一个巨大的缓存区对象。如果需要以对象的形式缓存大量数据,或者是注意到了长时间的垃圾回收暂停,
可以考虑配置这个选项。这些暂停时间可以在应用界面中显示的每个任务的垃圾回收时间那一栏看到。