spark学习笔记之spark core 取topN
初入职场的菜鸟,业务上遇到一个取topN的问题,要求是分组取app top5,这些app数据存储在Hive表里分属在5个不同字段,也就是说一条用户数据里有5个app,例如:
北京|qq|微信|手机百度|微博|淘宝
北京|微信|京东|头条|微博|appstore
上海|qq|支付宝|手机百度|微博|淘宝
上海|优酷|饿了么|||
...
根据业务需求,本来利用sparl-sql的开窗函数就可以一条sql语句轻松解决,但是公司的Spark集群版本太低,不支持开窗,无奈之下写程序来解决,为防止后续再遇此类问题,因此做笔记以记录
闲篇扯完我们来进入正题:
思路:
步骤一:统计每个市下的每个app的总数,统计成如下形式:
北京|qq,888
北京|微信,999
广州|支付宝,777
北京|京东,567
上海|淘宝,666
上海|qq,999
北京|微博,789
深圳|头条,555
...
从文件获取RDD<String>:
SparkConf conf = new SparkConf()
.setAppName("sparktest")
.setMaster("local");
@SuppressWarnings("resource")
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> lines = sc.textFile("d:/test.txt")
JavaPairRDD<String, Integer> multipleRDD = lines.flatMapToPair(new PairFlatMapFunction<String, String, Integer>() {
private static final long serialVersionUID = 1L;
/**
* PairFlatMapFunction类 包含一个返回Iterable迭代器的抽象方法,精华在于此,迭代器可迭代包含多个Tuple2的List集合
*/
@Override
public Iterable<Tuple2<String, Integer>> call(String line) throws Exception {
final String[] lineSplited = line.split("\\|");
Iterable<Tuple2<String, Integer>> iterable = new Iterable<Tuple2<String,Integer>>() {
@Override
public Iterator<Tuple2<String, Integer>> iterator() {
List<Tuple2<String, Integer>> tuple2s = new ArrayList<Tuple2<String, Integer>>();
String preKey = lineSplited[0];
/**
* 遍历切分好的字段,判断该字段是那个信息,
* 如:下表1到5为app;把信息和preKey组成需要的key,相当于wordCount中的word
*/
for (int i = 0; i < lineSplited.length; i++) {
if(i >=1 && i <= 5){
/**
* 单个用户的appTop5
*/
tuple2s.add(new Tuple2<String, Integer>(preKey+"|"+lineSplited[i],Integer.valueOf(1)));
}
}
return tuple2s.iterator();
}
};
return iterable;
}
}).reduceByKey(new Function2<Integer, Integer, Integer>() {
private static final long serialVersionUID = 1L;
@Override
public Integer call(Integer i1, Integer i2) {
return i1 + i2;
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
步骤二:对步骤一得出的汇总结果以市作为key分组:
----------
北京,qq|888
北京,微信|999
北京,京东|567
北京,微博|789
...
----------
上海,淘宝|666
上海,qq|999
...
----------
...
JavaPairRDD<String,String> grouped = multipleRDD.mapToPair(new PairFunction<Tuple2<String, Integer>, String, String>() {
private static final long serialVersionUID = 1L;
@Override
public Tuple2<String, String> call(Tuple2<String, Integer> arg0) throws Exception {
String[] string = arg0._1.split("\\|");
String str1 = string[0];
return new Tuple2<String, String>(str1,string[1]+"|"+arg0._2);
}
}).groupByKey();
步骤三:对步骤二得出的结果分市从大到小排序并取top5组成以”|”分隔的字符串:
----------
北京,微信|999
北京,qq|888
北京,微博|789
北京,京东|567
...
----------
上海,qq|999
上海,淘宝|666
...
----------
...
北京|微信|qq|微博|京东|淘宝
上海|微博|qq|微信|京东|手机百度
...
JavaPairRDD<String, String> groupedTopN = grouped.mapToPair(new PairFunction<Tuple2<String,Iterable<String>>, String, String>() {
private static final long serialVersionUID = 1L;
/**
* 这里的关键是组合字段和排序
* list.sort(new Comparator),用比较器排序
*/
@Override
public Tuple2<String, String> call(Tuple2<String, Iterable<String>> arg0) throws Exception {
Iterator<String> iterable = arg0._2.iterator();
/**
* 最终字符串信息初始化,
*/
String str = arg0._1;
List<String> list1 = new ArrayList<String>();
List<String> appTop5;
/**
* 迭代一个key,得到其下的所有value
*/
while (iterable.hasNext()) {
String[] strings =iterable.next().split("\\|");
list1 = Arrays.asList(strings);
}
/**
* app排序
*/
Collections.sort(list1, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int i1 = Integer.parseInt(o1.split("\\|")[1]);
int i2 = Integer.parseInt(o2.split("\\|")[1]);
return -(i1 - i2);
}
});
/**
* 取appTop5
*/
if(list1.size()<5){
appTop5 = list1;
}else{
appTop5 = list1.subList(0, 5);
}
for (String string : apps) {
str +="|"+string;
}
return new Tuple2<String, String>(arg0._1,str);
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
步骤四:持久化到HDFS入Hive表或到控制台
/**
* 输出到控制台
*/
groupedTopN.foreach(new VoidFunction<Tuple2<String,String>>() {
@Override
public void call(Tuple2<String, String> arg0) throws Exception {
System.out.println(arg0._2);
}
});
/**
* 输出到文件
*/
finalRDD.rdd().saveAsTextFile(outputPath);
需要的jar包: