Spark中的核心数据操作:
Note:
RDD是Spark数据操作的核心,它的主要特点是操作链,惰性求值。
创建RDD主要有两种方法:
JavaRDD<String> lines = sc.textFile("your file path")
JavaRDD<String> lines = sc.parralelize(Arrays.asList("pandas","i like pandas"));
RDD的操作可以分为两种操作:1. 转换操作,2. 行动操作。
Note:
Spark里面主要通过谱系图(lineage graph)来记录RDD的操作依赖链关系。通
过这种方式,Spark可以实现按需计算转换操作,也可在持久化的RDD丢失部分数据的情况时恢复所丢失的数据。
常用的转换操作有:
filter() //过滤数据集
map() //依次对数据集中每个数据进行操作
flatmap() //将函数应用于RDD的每个元素,将返回的迭代器的所有内容构成新的RDD,通常用来切分单词
distinct() //去重
sample() //采样
union() //求并集
intersection() //求交集
substract() //求子集
cartesian() //求笛卡尔子集
示例:
//filter
JavaRDD<String> errorsRDD = inputRDD.filter(
new Function<String, Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
}
);
RDD的行动操作会对数据集进行实际计算,它会根据RDD的依赖操作链进行优化,然后根据优化后的依赖操作链上的转换操作依次对数据进行转换操作,最后计算出结果返回到驱动器程序或者写入外部存储系统中。
常用的行动操作有:
count() //返回结果总数
take(num) //返回一部分结果
collect() //返回所有结果
reduce() //聚合
aggregate() //累加
示例:
1.求和
Integer sum = rdd.reduce(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer x, Integer y) { return x+y; }
});
2.计算平均值
class AvgCount implements Serializable {
public AvgCount(int total, int num) {
this.total = total;
this.num = num;
}
public int total;
public int num;
public double avg() {
return total / (double) num;
}
};
Function2<AvgCount, Integer, AvgCount> addAndCount = new Function2<AvgCount,Integer, AvgCount>() {
public AvgCount call(AvgCount a, Integer x) {
a.total +=x;
a.num += 1;
return a;
}
};
Function2<AvgCount, AvgCount, AvgCount> combine = new Function2<AvgCount,AvgCount,AvgCount>() {
public AvgCount call(AvgCount a, AvgCount b) {
a.total += b.total;
a.num += b.num;
return a;
}
}
AvgCount initial = new AvgCount(0,0);
AvgCount result = rdd.aggreate(initial, addAndCount, combine);
Note:
除非我们对RDD的中间结果进行了持久化操作,每当我们调用一个新的行动操作时,整个RDD都会从头计算。
Spark
中很多操作都需要依赖用户传递的函数。用Java
传递用户函数,主要实现Spark
中的org.apache.api.java.function
中任一函数接口的对象来进行传递的。该包中主要提供了三个接口。函数名 | 实现方法 | 用途 |
---|---|---|
Function |
R call(T) | 接收一个输入值并返回一个输出值,用于类似map()和filter等操作中 |
Function2 |
R call(T1,T2) | 接收两个输入值并返回一个输出值,用于类似aggregate()和fold()等操作中 |
FlatMapFunction |
Iterable call(T) | 接收一个输入值并返回任意个输出,用于类似flatMap()这样的操作中 |
具体的实现主要有三种方法:
RDD<String> errors = lines.filter(new Function<String,Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
});
class ContainsError implements Function<String, Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
}
RDD<String> errors = lines.filter(new ContainsError());
3.lambda表达式
RDD<String> errors = lines.filter(s -> s.contains("error"));
1.计算平方值
JavaRDD<Integer> rdd = sc.parrallelize(Arrays.asList(1,2,3,4));
JavaRDD<Integer> result = rdd.map(new Function<Integer, Integer>() {
public Integer call(Integer x) { return x*x; }
});
System.out.println(StringUtils.join(result.collect(),","));
JavaRDD<String> lines = sc.parallelize(Arrays.asList("hello world", "hi"));
JavaRDD<String> words = llines.flatMap(new FlatMapFunction<String, String>() {
public Iterable<String> call(String line) {
return Arrays.asList(line.split(" ")));
}
});
words.first();
RDD也支持一些集合操作,但是并不是严格意义上的集合操作,它并不符合集合的唯一性。因为有些操作RDD并不会对结果集合进行去重,需要手动进行distinct()
操作进行去重。
操作名 | 作用 | 是否进行数据混洗(去重) |
---|---|---|
distinct | 去重 | 是 |
union | 求并集 | 否 |
intersection | 求交集 | 是 |
substract | 求差集 | 是 |
cartesian | 求笛卡尔集 |
Note:
distinct()
,intersection()
和substract()
都需要进行数据混洗进行去重,开销会比较大。
鉴于有些函数只能用于特定类型,比如 mean()
和 variance()
只能用于数值,而 join()
只能用于键值对类型。Spark中除了提供通用的RDD,还提供了些特殊RDD,比如:DoubleRDD
(java中的JavaDoubleRDD
)和PairRDD
(java中的JavaPairRDD
)。在java
中使用spark
和scala
不同的是,spark
不会像在scala
中一样对RDD
进行隐性转换,我们需要在代码显性转换RDD
的类型。
示例:
将上面的计算平均值改写成为一个JavaDoubleRDD
版本的计算每个元素的平方值的示例。
//将一个mapRDD类型转换为一个DoubleRDD类型
JavaDoubleRDD result = rdd.mapToDouble(new DoubleFunction<Integer>() {
public double call(Integer x) {
return (double) x*x;
}
});
System.out.println(result.mean());
其他相关类型之间的转换:
方法 | 用途 |
---|---|
flatMapToDouble | 将flatMap类型转换成DoubleRDD |
mapToDouble | 将一个map类型转换成DoubleRDD |
flatMapToPair | 将flatMap类型转换成PairRDD |
mapToPair | 将map类型转换成PairRDD |