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 | 查看统计结果 |
RDD依赖关系
在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
内部,可以无顾虑地使用并行计算了。