《数据算法Hadoop/Spark》读书笔记1--二次排序

1 说明

本文档介绍Spark的二次排序解决方案

本章知识


方法返回类型/描述
textFile–> JavaRDD

- JavaRDD<String>
        org.apache.spark.api.java.JavaSparkContext.textFile(String path)
- JavaRDD<String> textFile(String path, int minPartitions)
mapToPair–> JavaPairRDD

- static <K2,V2> JavaPairRDD<K2,V2>
        mapToPair(PairFunction<T,K2,V2> f)
groupByKey–>JavaPairRDD-

-JavaPairRDD<K,Iterable<V>>
        org.apache.spark.api.java.JavaPairRDD.groupByKey()
mapValues要复制一份values再操作

- <U> JavaPairRDD<K,U>
        mapValues(Function<V,U> f)
说明: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.
IterableList可以使用第三方包
Lists.newArrayList(e)
com.google.common.collect.Lists.newArrayList
(Iterable elements)
take(n)
collect()
-static java.util.List<T> take(int num)
-static java.util.List<T> collect()

1.1 Chapter 01: Secondary Sorting With Spark

输入格式

每一条记录(行)格式如下

<key><,><time><,><value>

假设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

项目详细实现见如下过程,项目结构如下
这里写图片描述

1.1.1 新建maven工程

新建maven工程,坐标 <groupId>cn.whbing.spark</groupId>
<artifactId>dataalgorithms</artifactId>

并添加两个依赖,其中spark-core_2.11,是必须的;guava仅仅是为了将Iterable转化为List。

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.11</artifactId>
    <version>2.1.2</version>
</dependency>

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>24.1-jre</version>
</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.0</modelVersion>

  <groupId>cn.whbing.spark</groupId>
  <artifactId>dataalgorithms</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>dataalgorithms</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
        <version>2.1.2</version>
    </dependency>

    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>24.1-jre</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

1.1.2 编辑输入文件

输入文件每一行格式如下<key><,><time><,><value>,其保存在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.1.3 二次排序

Created with Raphaël 2.1.2 输入文件timeseries.txt SparkConf及SparkContext ① textFile将每一行读取成->JavaRDD,即lines ② mapToPair(new PairFunction<T,K,V>) -> JavaPairRDD ③ groupByKey -> JavaPairRDD,即groups ④ mapValues->对groups中的Iterable类型的Vaule排序

/ 1 / 说明

textFile将每一行读取成RDD可一参数或两参数;
mapToPair(new PairFunction<T,K,V>)对每一行RDD的处理。T:原RDD类型,即StringK:转化后Key类型;V:转化后Value类型;
注:new PairFunction中返回Tuple2<K,V>即可对应到PairRDD
groupByKey将相同keyvalue值组成Iterable
mapValues排序上述Iterable,属于java中处理集合范畴

/ 2 / 几个重要过程介绍

(1)处理输入文件并读取

将输入作为运行参数

//读取参数并验证
if(args.length<1){
    System.err.println("Usage: SecondarySort <file>");
    System.exit(1);  //1表示非正常退出,0表示正常退出
}

//读取输入的参数,即输入文件
String inputPath = args[0];
System.out.println("args[0]:<file>="+args[0]);

创建sparkConfsparkContext,并读取文件

SparkConf conf = new SparkConf().setAppName("SecondarySort by spark").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> lines = sc.textFile(inputPath);

运行时需要传入参数。eclipseRun Configurations–>Arguments填入timeseries.txt

(2)中间结果调试collect或take

想要打印中间过程的RDDPairRDD,均使用collect。(saveAsText保存为文本,后续介绍)
take(int num)只打印前num个

spark2.1.2 API 如下

static java.util.List<T>    take(int num) 
static java.util.List<T>    collect() 

PairRDD<K,V> collect后变成List<Tuple2<K,V>>

如对分组后的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<Tuple2<Integer, Integer>> 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<Integer, Integer> t1, 
            Tuple2<Integer, Integer> t2) {
        if(t1._1 <t2._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 <file>");
            System.exit(1);  //1表示非正常退出,0表示正常退出
        }

        //读取输入的参数,即输入文件
        String inputPath = args[0];
        System.out.println("args[0]:<file>="+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();

        //读取每一行即<name><,><time><,><value>
        //JavaRDD<String> lines = sc.textFile("timeseries.txt");
        JavaRDD<String> lines = sc.textFile(inputPath);

        //将每一行的JavaRDD<String>包括<name><,><time><,><value>进行进一步的处理
        //转化成键值对,键是name,值是Tuple2(time,value)
        /*PairFunction<T,K,V> 
          T-->表示输入
          K-->表示转化后的key
          V-->转化后的value
        */
        /*
        public interface PairFunction<T, K, V> extends Serializable {
            Tuple2<K, V> call(T t) throws Exception;
        }    
        */
        JavaPairRDD<String, Tuple2<Integer,Integer>> pairs = 
                lines.mapToPair(new PairFunction<String, String, Tuple2<Integer,Integer>>() {

                    @Override
                    public Tuple2<String, Tuple2<Integer, Integer>> call(String s) throws Exception {
                        //对输入s进行转换
                        String[] tokens = s.split(",");//s即x,2,5 以逗号分割
                        System.out.println(tokens[0]+","+tokens[1]+","+tokens[2]);
                        Integer time = new Integer(tokens[1]);//parseInt,valueOf都可以
                        Integer value = new Integer(tokens[2]);
                        Tuple2<Integer,Integer> timevalue = new Tuple2<Integer,Integer>(time,value);
                        return new Tuple2<String,Tuple2<Integer, Integer>>(tokens[0],timevalue);
                    }
                }); 
        //验证上述中间RDD的结果
        List<Tuple2<String, Tuple2<Integer,Integer>>> out1 =  
                pairs.take(5);//collect
        System.out.println("==DEBUG1==");
        System.out.println("JavaPairRDD collect as:");
        for(Tuple2 t:out1){
            System.out.println(t._1+","
                    +((Tuple2<Integer, Integer>)t._2)._1+","
                    +((Tuple2<Integer, Integer>)t._2)._2);
        }

        //对name进行分组groupByKey,分组之后相同key显示一个,value汇集在一起为Iterable
        JavaPairRDD<String,Iterable<Tuple2<Integer,Integer>>> groups = 
                pairs.groupByKey();

        //对groups进行验证,使用collect收集结果,存为List,里面PairRDD记为Tuple2型
        System.out.println("==DEBUG2==");
        System.out.println("groupByKey as:");
        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("----");
        }

        //groups中的value是Iterable类型,每一条数据是Tuple2,需要对其排序
        //比较器件util.TupleComparator
        //new Function参数,前一个是输入,后一个是输出
        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<Tuple2<Integer, Integer>> listOut = new ArrayList<>((ArrayList)v);

                        ArrayList<Tuple2<Integer, Integer>> listOut =Lists.newArrayList(v);

                        Collections.sort(listOut,TupleComparator.INSTANCE);//new TupleComparator()
                        return listOut; //listOut属于Iterable,可以返回

                    }               
        });

        //输出sorted
        System.out.println("==DEBUG OUTPUT==");
        List<Tuple2<String, Iterable<Tuple2<Integer, Integer>>>> out = 
                sorted.collect();
        for(Tuple2<String, Iterable<Tuple2<Integer, Integer>>> t:out){
            System.out.println(t._1);//key
            for(Tuple2<Integer, Integer> t2:t._2){
                System.out.println(t2._1+","+t2._2);
            }
            System.out.println("----");
        }
    }
}

1.1.4 运行结果

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
----

1.1.5 小结

  1. textFile –> JavaRDD
  2. mapToPair –> JavaPairRDD
  3. groupByKey –>JavaPairRDD
  4. mapValues–>要复制一份values再操作
  5. Iterable转List
  6. take(n)collect()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值