本文档介绍Spark的二次排序解决方案。
方法 | 返回类型/描述 |
---|---|
textFile |
–> JavaRDD - JavaRDD org.apache.spark.api.java.JavaSparkContext. textFile (String path)- JavaRDD textFile (String path, int minPartitions) |
mapToPair |
–> JavaPairRDD - static JavaPairRDD mapToPair (PairFunction f) |
groupByKey |
–>JavaPairRDD- - JavaPairRDD org.apache.spark.api.java.JavaPairRDD.groupByKey() |
mapValues |
要复制一份values再操作 - JavaPairRDD mapValues(Function 说明:Pass each value in the key-value pair RDD through a map function without changing the keys; this also retains the original RDD’s partitioning. |
Iterable 转 List |
可以使用第三方包Lists.newArrayList(e) com.google.common.collect.Lists.newArrayList (Iterable elements) |
take(n) 及 collect() |
-static java.util.List take(int num) - static java.util.List collect() |
输入格式
每一条记录(行)格式如下
<,>
假设key
已经有序,对key
分组,对time
排序
输入数据
$ cat chap01-timeseries.txt
p,4,40
p,6,20
x,2,9
y,2,5
x,1,3
y,1,7
y,3,1
x,3,6
z,1,4
z,2,8
z,3,7
z,4,0
p,1,10
p,3,60
期望输出
(z,{1=>4, 2=>8, 3=>7, 4=>0})
(p,{1=>10, 3=>60, 4=>40, 6=>20})
(x,{1=>3, 2=>9, 3=>6})
(y,{1=>7, 2=>5, 3=>1})
环境
spark version 2.1.2-SNAPSHOT
Java 1.8.0_131
新建maven工程,坐标
并添加两个依赖,其中spark-core_2.11
,是必须的;guava
仅仅是为了将Iterable转化为List。
<dependency>
<groupId>org.apache.sparkgroupId>
<artifactId>spark-core_2.11artifactId>
<version>2.1.2version>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>24.1-jreversion>
dependency>
整个pom
文件如下
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.whbing.sparkgroupId>
<artifactId>dataalgorithmsartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>dataalgorithmsname>
<url>http://maven.apache.orgurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.apache.sparkgroupId>
<artifactId>spark-core_2.11artifactId>
<version>2.1.2version>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>24.1-jreversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>3.8.1version>
<scope>testscope>
dependency>
dependencies>
project>
输入文件每一行格式如下
,其保存在chap01-timeseries.txt
中,内容如下。输入文件的位置可以保存在本地或HDFS
等。本测试在local
模式运行(集群类似),chap01-timeseries.txt
文件位置与src
同级。输入文件可以写死,运行时直接运行即可;也可以将输入文件作为参数输入,运行参数为chap01-timeseries.txt
。
chap01-timeseries.txt
p,4,40
p,6,20
x,2,9
y,2,5
x,1,3
y,1,7
y,3,1
x,3,6
z,1,4
z,2,8
z,3,7
z,4,0
p,1,10
p,3,60
/ 1 / 说明
① textFile
将每一行读取成RDD可一参数或两参数;
② mapToPair(new PairFunction
对每一行RDD的处理。T
:原RDD类型,即String
;K
:转化后Key
类型;V
:转化后Value
类型;
注:new PairFunction
中返回Tuple2
即可对应到PairRDD;
③ groupByKey
将相同key
的value
值组成Iterable
;
④ mapValues
排序上述Iterable
,属于java
中处理集合
范畴
/ 2 / 几个重要过程介绍
(1)处理输入文件并读取
将输入作为运行参数
//读取参数并验证
if(args.length<1){
System.err.println("Usage: SecondarySort " );
System.exit(1); //1表示非正常退出,0表示正常退出
}
//读取输入的参数,即输入文件
String inputPath = args[0];
System.out.println("args[0]:=" +args[0]);
创建sparkConf
及sparkContext
,并读取文件
SparkConf conf = new SparkConf().setAppName("SecondarySort by spark").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> lines = sc.textFile(inputPath);
运行时需要传入参数。eclipse
中Run Configurations
–>Arguments
填入timeseries.txt
(2)中间结果调试collect或take
想要打印中间过程的RDD
或PairRDD
,均使用collect
。(saveAsText保存为文本,后续介绍)
take(int num)
只打印前num个
spark2.1.2 API 如下
static java.util.List take(int num)
static java.util.List collect()
PairRDD
collect
后变成List
如对分组后的PairRDD
调试
List<Tuple2<String,Iterable<Tuple2<Integer,Integer>>>> out2 = groups.collect();
for(Tuple2<String,Iterable<Tuple2<Integer,Integer>>> t:out2){
System.out.println(t._1);
Iterable<Tuple2<Integer,Integer>> list = t._2;
for(Tuple2<Integer,Integer> t2:list){
System.out.println(t2._1+","+t2._2);
}
System.out.println("----");
}
(3)对Iterable排序
RDD不可变,进行操作需要先复制一份。
不能直接将Iterable转ArrayList,需要转换。这里可以使用import com.google.common.collect.Lists;
JavaPairRDD<String,Iterable<Tuple2<Integer,Integer>>> sorted =
groups.mapValues(new Function<Iterable<Tuple2<Integer,Integer>>,
Iterable<Tuple2<Integer,Integer>>>() {
@Override
public Iterable<Tuple2<Integer, Integer>> call(Iterable<Tuple2<Integer, Integer>> v)
throws Exception {
//直接对v排序是错误的,因为RDD不可变,要复制一份
//Collections.sort(v, new TupleComparator());
//书中有错误,需要转化v成ArrayList,不能直接是iterable
//以下转化也有问题,引入google的包
//ArrayList> listOut = new ArrayList<>((ArrayList)v);
ArrayList<Tuple2<Integer, Integer>> listOut =Lists.newArrayList(v);
Collections.sort(listOut, new TupleComparator());
return listOut; //listOut属于Iterable,可以返回
}
});
(4)定制比较器
可以使用单例模式
TupleComparator.java
package cn.whbing.spark.dataalgorithms.chap01.util;
import java.io.Serializable;
import java.util.Comparator;
import scala.Tuple2;
public class TupleComparator implements
Comparator<Tuple2<Integer,Integer>>,Serializable {
public static final TupleComparator INSTANCE = new TupleComparator();
@Override
public int compare(Tuple2 t1,
Tuple2 t2) {
if(t1._1 return -1;
}else if(t1._1 >t2._1){
return 1;
}
return 0;
}
}
/ 3 / 完整代码
SecondarySort.java
package cn.whbing.spark.dataalgorithms.chap01;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;
import com.google.common.collect.Lists;
import cn.whbing.spark.dataalgorithms.chap01.util.TupleComparator;
import scala.Tuple2;
public class SecondarySort {
public static void main(String[] args) {
//读取参数并验证
if(args.length<1){
System.err.println("Usage: SecondarySort " );
System.exit(1); //1表示非正常退出,0表示正常退出
}
//读取输入的参数,即输入文件
String inputPath = args[0];
System.out.println("args[0]:=" +args[0]);
//创建sparkConf及sparkContext(集群模式下运行时配模式时sparkConf可以不要)
SparkConf conf = new SparkConf().setAppName("SecondarySort by spark").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
//final JavaSparkContext sc = new JavaSparkContext();
//读取每一行即<,>
p,4,40
p,6,20
x,2,9
y,2,5
x,1,3
==DEBUG1==
JavaPairRDD collect as:
p,4,40
p,6,20
x,2,9
y,2,5
x,1,3
==DEBUG2==
groupByKey as:
p,4,40
p,6,20
x,2,9
y,2,5
x,1,3
y,1,7
y,3,1
x,3,6
z,1,4
z,2,8
z,3,7
z,4,0
p,1,10
p,3,60
z
1,4
2,8
3,7
4,0
----
p
4,40
6,20
1,10
3,60
----
x
2,9
1,3
3,6
----
y
2,5
1,7
3,1
----
==DEBUG OUTPUT==
z
1,4
2,8
3,7
4,0
----
p
1,10
3,60
4,40
6,20
----
x
1,3
2,9
3,6
----
y
1,7
2,5
3,1
----
textFile
–> JavaRDDmapToPair
–> JavaPairRDDgroupByKey
–>JavaPairRDDmapValues
–>要复制一份values再操作take(n)
及collect()