Spark数据存储的核心是弹性分布式数据集(RDD)。RDD可以被抽象地理解为一个大的数组,这个数组中的每个元素是RDD逻辑上的一个分区(partition),每个分区分布在集群上的不同节点上。



在spark的执行过程中,RDD经过transformation算子之后,最后由action算子触发操作。逻辑上每经历一次转换,就会将RDD转换为一个新的RDD,新的RDD和旧的RDD之间通过lineage产生依赖关系,这个关系在容错中有很重要的作用,而依赖也分为宽依赖和窄依赖。转换算子的输入和输出都是RDD,RDD会被划分成很多的分区分布到集群的多个节点中。分区是个逻辑概念,变换前后的新旧分区在物理上可能是同一块内存存储。这是很重要的优化,以防止函数式数据不变性(https://www.ibm.com/developerworks/cn/导致的内存需求无限扩张



在物理上,RDD对象实质上是一个元数据结构,存储着Block、node等的映射关系,以及其他的元数据信息。一个RDD就是一组分区,在物理数据存储上,RDD的每个分区对应的就是一个Block,Block可以存储在内存,当内存不够时可以存储到磁盘上。



每个Block中存储着RDD所有数据项的一个子集,暴露给用户的可以是一个Block的迭代器(例如,用户可以通过mappartition获得分区迭代器进行操作),也可以就是一个数据项(例如,通过map函数对每个数据项并行计算)。



如果是从HDFS等外部存储作为输入数据源,数据按照HDFS中的数据分布策略进行数据分区,HDFS的一个Block对应spark的一个分区。同时spark支持重分区,数据通过spark默认或者用户自定义的分区器决定数据块分布在哪些节点。例如,支持hash分区和range分区(将属于同一数据范围的数据放入同一分区)等分区策略。



如果RDD计算的结果需要重用,就需要对这些数据进行持久化存储,这就需要使用cache()函数,将数据缓存在内存中,这样下次需要使用时可以直接使用不需要重新计算了。cache()函数是persist()函数MEMORY_ONLY级别的函数,表示只存储在内存中。当然有时候,需要持久化存储的数据比较大,内存就会吃不消,这时也可以设置存储到磁盘上,也可以设置以序列化后的形式存储,另外叙说。