Sprak RDD 持久化
Spark中最重要的功能之一是跨操作在内存中持久化(或缓存)数据集。当您持久保存RDD时,每个节点都会存储它在内存中计算的任何分区,并在该数据集(或从中派生的数据集)的其他操作中重用它们。这使得未来的行动更快(通常超过10倍)。
您可以使用persist()或cache()方法标记要保留的RDD 。第一次在动作中计算它,它将保留在节点的内存中。Spark的缓存是容错的 - 如果丢失了RDD的任何分区,它将使用最初创建它的转换自动重新计算。
cache() 默认调用的 persist()
/**Persist this RDD with the default storage level (
MEMORY_ONLY
).*/
def cache(): this.type = persist()
存储级别
通过传递StorageLevel对象(Scala, Java, Python)来设置这些级别 persist()。该cache()方法是使用默认存储级别的简写,即StorageLevel.MEMORY_ONLY(在内存中存储反序列化的对象)
MEMORY_ONLY 将RDD存储为JVM中的反序列化Java对象。如果RDD不适合内存,则某些分区将不会被缓存,并且每次需要时都会重新计算。这是默认级别。
MEMORY_AND_DISK 将RDD存储为JVM中的反序列化Java对象。如果RDD不适合内存,请存储不适合磁盘的分区,并在需要时从那里读取它们。
MEMORY_ONLY_SER 将RDD存储为序列化 Java对象(每个分区一个字节数组)。这通常比反序列化对象更节省空间,特别是在使用 快速序列化器时,但读取CPU密集程度更高。
数据序列化
Spark 提供了两个序列化库:
Java序列化:默认情况下,Spark使用Java ObjectOutputStream框架序列化对象,并且可以与您创建的任何类一起使用 java.io.Serializable。您还可以通过扩展来更紧密地控制序列化的性能 java.io.Externalizable。Java序列化是灵活的,但通常很慢,并导致许多类的大型序列化格式。
Kryo序列化:Spark还可以使用Kryo库(版本4)更快地序列化对象。Kryo比Java序列化(通常高达10倍)显着更快,更紧凑,但不支持所有 Serializable类型,并且需要您提前注册您将在程序中使用的类以获得最佳性能。
Kryo序列化使用
val conf = new SparkConf().setMaster(…).setAppName(…)
//启用Kryo 序列化方式
conf.set(“spark.serializer”, “org.apache.spark.serializer.KryoSerializer”)
//注册需要序列化的类
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)
序列化效率对比
普通java序列化 MEMORY_ONLY
普通java序列化 MEMORY_ONLY_SER
KryoSerializer 不注册类直接查看
把自己的类注册到Kryo序列化里:
什么时候需要持久化
1,某步骤计算特别耗时;
2,计算链条特别长;
3,checkpoint所在的RDD也一定要persist(在checkpoint之前,手动进行persist)持久化数据,为什么?checkpoint的工作机制,是lazy级别的,在触发一个作业的时候,开始计算job,job算完之后,转过来spark的调度框架发现RDD有checkpoint标记,转过来框架本身又基于这个checkpoint再提交一个作业,checkpoint会触发一个新的作业,如果不进行持久化,进行checkpoint的时候会重算,如果第一次计算的时候就进行了persist,那么进行checkpoint的时候速度会非常的快。
4,shuffle之后;
注意
使用Cache注意下面三点
(1)cache之后一定不能立即有其它算子,不能直接去接算子。因为在实际工作的时候,cache后有算子的话,它每次都会重新触发这个计算过程。
(2)cache不是一个action,运行它的时候没有执行一个作业。
(3)cache缓存如何让它失效:unpersist,它是立即执行的。persist是lazy级别的(没有计算),unpersist时eager级别的。