1.1 简介
spark以RDD(弹性分布式数据集)为基本的数据结构,并定义了RDD相关的基本操作:Map、Filter、flatMap、groupByKey和Union等。spark第一次启动时需要把数据载入到内存,之后的中间结果保存在内存中,避免从硬盘进行读写,因此后期的迭代计算速度很快,速度是spark的最大优势。
在任务(task)级别上,spark的并行机制是多线程模型,同一节点上的任务以多线程的方式运行在一个JVM进程中,可以带来更快的启动速度、更高的CPU利用率,以及更好的内存共享。
spark有五个主要的扩展库,分别是支持结构化数据的Spark SQL、处理实时数据的Spark Streaming、用于机器学习的MLlib、用于图计算的GraphX、用于统计分析的SparkR。
大部分应用中,spark依赖HDFS(分布式文件存储系统)和HBase(分布式数据库存储)来存储数据,依赖YARN来管理集群和资源。
1.2 RDD
RDD(弹性分布式数据集)是spark最基础的数据结构,将中间结果存储在内存而不是硬盘中以减少I/O操作。
1.2.1 基本特性与依赖关系
RDD表示已被分区、不可变的,并能够被并行操作的数据集合。其基本特性如下:
-
分区:同一个RDD包含的数据被存储在系统的不同节点,每个分区指向一个存放在内存或者硬盘中的数据块(Block),即RDD是抽象意义的数据集合,分区内部并不会存储具体的数据,因此可以对RDD进行repartition操作。
-
不可变性:RDD是只读的,例如RDD lines是只读的,其数据不可变,但可以通过map函数计算得到RDD lineLengths,这是一种中间转换操作,即依赖关系。
lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
- 并行性:由于分区而天然存在的特性,不太理解。
RDD包含两种主流的分区方式:Hash partitioner(对数据的Key进行散列分区)和 Range partitioner(按Key的排序进行均匀分区)。
RDD操作会记录依赖关系,即通过哪个父RDD经过哪个转换操作得到,依赖关系分为:窄依赖(父分区1——>子分区1,父分区唯一对应一个子分区,如map、filter等操作)、宽依赖(父分区1、父分区2——>子分区1,父分区1、父分区3——>子分区2,如join、groupby等操作)。注意:子RDD的一个分区可以使用父RDD的多个分区,只要满足被子RDD分区使用的父RDD分区不被子RDD的其他分区使用就算窄依赖。
1.2.2 检查点与持久化
基于RDD的依赖关系,当任意一个RDD在相应的节点发生数据丢失,只需从上一步的RDD出发再次计算,便可恢复该RDD。但当多个RDD出现故障,或某些RDD的操作比较复杂,计算耗时长,则可通过设置检查点Checkpoint,将该结果存至硬盘或HDFS。Checkpoint会清空该RDD的依赖关系,并新建一个CheckpointRDD依赖关系。
persist()和cache()方法支持将RDD的数据缓存至内存或硬盘,实现RDD的持久化。持久化RDD会保存依赖关系和计算结果至内存中。
1.2.3 迭代计算
RDD的计算分为2种:转换(Transformation,从父RDD产生子RDD)和动作(Action,返回一个计算结果)。转换操作生成新的 RDD,并且记录依赖关系,但spark 并不会立刻计算出新RDD中各个分区的数值,直到遇到一个动作时,数据才会被计算,并且输出结果给Driver。例如,对RDD进行map转换,再进行collect动作,这时map后生成的RDD不会立即被计算,只有当执行到collect操作时,map才会被计算,这是一种惰性设计。
- Collect:以数组的形式,返回RDD的所有元素
rdd = sc.parallelize(["b", "a", "c"]) //创建一个RDD分区
rdd.map(lambda x: (x, 1)).collect() // [('b', 1), ('a', 1), ('c', 1)]
1.3 spark SQL
Hive是数据仓库工具,将结构化的数据文件映射为一张数据库表,并提供类似SQL的编程接口,为spark进行数据查询。然而Hive的发展制约了spark的更新,为了解决这一困境,spark SQL被开发实现,其不仅将关系型数据库的处理模式和Spark的函数式编程相结合,还兼容多种数据格式,包括Hive、RDD、JSON、CSV等。
spark SQL允许数据仓库应用程序直接获取数据,并提供两个API:DataFrame API和 DataSet API。DataSet所描述的数据都被组织到有名称的列中,类似关系型数据库中的表结构,提供详细的结构信息与每列的数据类型,即schema信息。DataFrame结构类似。三者的区别如表所示。
DataFrame并不是类型安全的,因为它不存储每一列的信息如列名和数据类型(但是我实践pyspark时,读取csv文件确实定义了schema的列名和类型?)。