- 算子分类
- RDD依赖关系
Spark快速入门 算子分类
在spark中,将RDD的成员函数翻为算子(operator),我觉得叫操作也可以。根据算子返回值类型的不同,可主要分为转换(transformation)算子和动作(Action)算子,前者返回一个新的RDD,后者返回其他数据类型。
在下表中,如未作说明,则f表示函数,data表示另一个RDD,可选参数[num]表示并行任务个数;键值对格式为(key,value);seed表示随机数种子。
| 转换算子 | 返回一个新的RDD |
|---|---|
| map(f) | 返回的RDD由每一个输入元素经过f函数转换后组成 |
| filter(f) | 返回的RDD由经过f计算后返回值为true的输入元素组成 |
| flatMap(f) | 类似于map,但是一个输入可被映射为0或多个输出元素 |
| mapPartitions(f) | 类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U] |
| mapPartitionsWithIndex(f) | 类似于mapPartitions,但f带有分片的索引,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U] |
| sample(withReplacement, fraction, seed) | 根据fraction指定的比例对数据进行采样,可以选择是否使用随机数进行替换 |
| union(data) | 源RDD和参数RDD的并集 |
| intersection(data) | 源RDD和参数RDD的交集 |
| distinct([num])) | 去重 |
| groupByKey([num]) | RDD须为键值对形式,将key相同等value合并到一个列表中 |
| reduceByKey(func, [num]) | RDD须为键值对形式,聚合f(key)相同的value |
| aggregateByKey(zero, seqOp, combOp, [num]) | 聚合相同的Key值,zero为每次分组之后每组的初值,seqOp表示聚合函数,combOp表示在reduce端等聚合逻辑。 |
| sortByKey([ascending], [num]) | RDD须为键值对形式,通过key进行排序 |
| sortBy(f,[ascending], [num]) | 根据函数f进行排序 |
| join(data,[num]) | RDD须为键值对形式,将源和参数RDD等value通过key值组成一个对 |
| cogroup(data, [num]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD |
| cartesian(data) | 笛卡尔积 |
| pipe(command, [envVars]) | 对rdd进行管道操作 |
| coalesce(num) | 减少分区数到num,可在过滤大量数据之后执行此操作 |
| repartition(num) | RDD分区重新定为num |
| 动作算子 | |
|---|---|
| reduce(func) | 通过func函数聚集RDD中的所有元素,这个功能必须是可交换且可并联的 |
| collect() | 以数组形式返回数据集的所有元素 |
| count() | 返回元素个数 |
| first() | 返回第一个元素 |
| take(n) | 返回前n个元素组成的数组 |
| takeSample(w, num, [seed]) | 选取num个元素,作为数组返回。 |
| takeOrdered(n, [ordering]) | 返回自然顺序或者自定义顺序的前n个元素 |
| saveAsTextFile(path) | 将数据集的元素通过toString转为文本,然后以textfile的形式保存到支持的文件系统 |
| saveAsSequenceFile(path) | 保存为Hadoop sequencefile格式到指定目录,需为Hadoop支持的文件系统 |
| saveAsObjectFile(path) | 以Java序列化的方式保存到path |
| countByKey() | RDD须为键值对形,返回每个key对应相同元素等个数 |
| foreach(f) | 对每一个元素使用函数f更新 |
| foreachPartition(f) | 在每一个分区上,运行函数func |
| 统计算子 | 含义 |
|---|---|
| count | 个数 |
| mean | 均值 |
| sum | 求和 |
| max | 最大值 |
| min | 最小值 |
| variance | 方差 |
| sampleVariance | 从采样中计算方差 |
| stdev | 标准差:衡量数据的离散程度 |
| sampleStdev | 采样的标准差 |
| stats | 查看统计结果 |
在Spark中,RDD并不实际存储数据,而只是记录了数据的位置以及转换关系,只有要求返回结果时,这些转换才会真正运行。
由于每个转换操作都会生成一个新的RDD,所以RDD之间会形成某种依赖关系。在Spark中,依赖分为两种:宽依赖和窄依赖,二者的区别在于:
- 窄依赖:父RDD的一个分区只会被子RDD的一个分区依赖
- 宽依赖:父RDD的一个分区会被子RDD的多个分区依赖
RDD的依赖关系是其计算次序的反映,每个RDD相当于计算流程中的一个节点,而转换算子相当于是一条线,或者说是一条边。从而由RDD和算子组成的计算网络,就组成了一个有向无环图(Directed Acyclic Graph, DAG)。
由于动作算子并不返回RDD,所以每次进行Action,都相当于是这个DAG的一个终结符号。
假设现有如下处理过程
很容易发现,在宽依赖情况下,新的RDD继承了多个父节点,换句话说,RDD中的任何一个元素的计算都涉及到此前所有元素的计算结果。这个过程被形象地称为shuffle,即洗牌。
假设P1计算得很快,那么当计算到reduceByKey这一步时必须等待P2、P3计算完成。根据这种特性,我们将整个处理流程分成了两个stage,这样,在stage内部,可以无顾虑地使用并行计算了。



