DataFrame是一个开创性的基于分布式的数据处理模式,脱离了单纯的MapReduce的分布设定、整合、处理模式,而采用一个新颖的、类似一般数组或集合的处理模式,对存储在分布式存储空间上的数据进行操作。
DataFrame可以看成一个分布在不同节点中的分布式数据集,并将数据以数据块(Block)的形式存储在各个节点的计算机中,整体布局如图3-2所示。DataFrame主要用于进行结构化数据的处理,提供一种基于RDD之上的全新概念,但是底层还是基于RDD的,因此这一部分基本上和RDD是一样的。
图3-2 DataFrame数据块存储方式
从图3-2可以看出,每个BlockMaster管理着若干个BlockSlave,而每个BlockSlave又管理着若干个BlockNode。当BlockSlave获得了每个Node节点的地址时,又反向BlockMaster注册每个Node的基本信息,这样会形成分层管理。
对于某个节点中存储的数据,如果使用频率较多,BlockMaster就会将其缓存在自己的内存中,如果以后需要调用这些数据,就可以直接从BlockMaster中读取。对于不再使用的数据,BlcokMaster会向BlockSlave发送一组命令予以销毁。
对于DataFrame来说,它有一个比RDD好的地方,就是可以使用对外内存,使内存的使用不会过载,比RDD有更好的执行性能。
宽依赖(wide dependency,也称shuffle dependency)与窄依赖(narrow dependency)是Spark计算引擎划分Stage的根源所在,遇到宽依赖就划分为多个Stage,并针对每个Stage提交一个TaskSet。这两个概念对于理解Spark的底层原理非常重要,所以做业务时不管是使用RDD还是DataFrame,都需要好好理解它们。
注意,Transformation在生成RDD的时候,生成的是多个RDD,但不是同时一次性生成。这里的RDD生成方式并不是一次性生成多个,而是由上一级的RDD依次往下生成,我们将其称为依赖。
RDD依赖生成的方式不尽相同,在实际工作中一般由两种方式生成:宽依赖和窄依赖,两者的区别如图3-3所示。
图3-3 宽依赖和窄依赖
RDD作为一个数据集合,可以在数据集之间逐次生成。如果每个RDD的子RDD只有一个父RDD,而同时父RDD也只有一个子RDD时,那么这种生成关系称为窄依赖,如窄依赖的矩形框里所示。如果多个RDD相互生成,就称为宽依赖,如宽依赖的矩形框里所示。
宽依赖和窄依赖在实际应用中有着不同的作用。窄依赖便于在单一节点上按次序执行任务,使任务可控。宽依赖更多的是考虑任务的交互和容错性。这里没有好坏之分,具体选择哪种方式需要根据具体情况处理。宽依赖往往对应着shuffle(模拟扑克牌中的洗牌操作)操作,需要在运行过程中将同一个父RDD的分区传入不同的子RDD分区中,中间可能涉及多个节点之间的数据传输;窄依赖的每个父RDD的分区只会传入一个子RDD分区中,通常可以在一个节点内完成。