• sparksql


    Spark SQL
    一、sparkSQL的特点
    1.支持多种数据源:hive RDD Partquet JSON JDBC
    2.多种性能优化技术:in-memory columnar storage  byte-code generation  cost model 动态评估
    3.组件扩展性:对于SQL的语法解析器、分析器、以及优化器,用户都可以自己重新开发,并且动态扩展
    
    Spark sql 的性能优化技术简介
    
    1.内存列存储(in-memory columnar storage)
    内存列存储意味着spark sql的数据,不是使用java对象的方式进行存储,而是使用面向列存储的方式进行存储,也就是说,每一列,作为一个数据存储的单位,从而大大优化内存使用的效率。使用列存储之后,减少对内存的消耗,也就避免了对GC(垃圾回收)大量数据的性能开销
    
    2.字节码生成技术(byte-code generation )
    Spark sql在其catalyst模块的Expressions中增加一codegen模块,对于sql语句中的计算表达式,比如select num + num from t 这种sql,就可以使用动态字节码生成技术来优化其性能。
    
    3.Scala代码编写的优化
    对于Scala代码编写中,可能会造成大量的性能的开销,自己重写,使用更加复杂的方式,来获取更好的性能。比如option样例类、for循环、map/filter/foreach等高阶函数,以及不可变对象,都改成用null,while循环来实现,并且重用可变的对象
    
    
    二、dataframe的使用
    1.spark sql 和 dataframe引言
    Spark sql 是spark中的一个模块,主要是进行结构化数据的处理。他提供的最核心的编程抽象,就是dataframe。同时spark sql 还可以作为分布式的sql查询引擎。Spark sql最重要的功能之一就是从hive中查询数据
    
    Dataframe,可以理解为时,以列的形式组织的,分布式的数据集合,他其实和关系型数据库中的表非常类似,但是底层做了很对的优化。Dataframe可以通过很多来源,包括:结构化数据文件,hive表,外部关系型数据库以及RDD
    
    
    2.SQLContext
    要使用spark sql ,首先就得创建一个SQLContext对象,或者是他的子类的对象(HiveContext),比如HiveContext对象;
    
    Java版本:
    JavaSparkContext sc = ....;
    SQLContext SQLContext = new SQLContext(sc);
    
    Scala版本:
    Val sc = SparkContext..
    Val SQLContext = new SQLContext(sc)
    Import SQLContext.implicits._
    
    3.HiveContext 
    除了基本的SQLContext以外,还可以使用它的子类---HiveContext。HiveContext的功能除了包含SQLContext提供的所有的功能外,还包括额外的专门针对hive的一些功能。这些额外的功能包括:使用hive语法编写和执行sql,使用hive的UDF函数,从hive表中读取数据
    
    要使用HiveContext,就必须预先安装好hive,SQLContext支持的数据源,HiveContext也同样支持,而不只是支持hive,对spark1.3.x以上的版本,都推荐使用HiveContext,因为其功能更加丰富和完善
    
    Spark sql 还支持使用spark.sql.dialect参数设置sql方言,使用SQLContext的serConf()即可进行设置。对与SQLContext,他只支持”sql”一种方言。对于HiveContext,它默认的方言是“hiveql”
    
    
    
    
    4.创建Dataframe
    使用SQLContext,可以从RDD、hive表中或者其他额数据源,来创建dataframe。
    
    Java版本:
    package com.spark.spark_sql;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.SQLContext;
    
    /**
     * 使用json文件创建dataframe
     * @author Administrator
     */
    public class DataFrameCreate {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setAppName("DataFrameCreate");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext SQLContext = new SQLContext(sc);
            DataFrame df = SQLContext.read().json("hdfs://hadoop01:8020/spark_input/students.json");
            df.show();
    
        }
    }
    
    
    提交至集群:运行打包,上传。编写脚本进行提交
    【这里是一台机器测试】
    bin/spark-submit 
    --class com.spark.spark_sql.DataFrameCreate 
    --files /opt/modules/apache-hive-0.13.1-bin/conf/hive-site.xml 
    --driver-class-path /opt/softwares/mysql-connector-java-5.1.27-bin.jar 
    /opt/modules/spark-1.6.1-bin-2.5.0-cdh5.3.6/sql/sparksql_01.jar
    
     运行的结果
    +---+---+--------+
    |age| id|    name|
    +---+---+--------+
    | 10|  1|     leo|
    | 25|  2|    kity|
    | 30|  4|    lucy|
    | 20|  3|     tom|
    | 18|  7|    jack|
    | 23| 10|  edison|
    | 36|  5|    owen|
    | 20|  8|    jiny|
    | 40|  6|    lisi|
    | 45|  9|zhangsan|
    +---+---+--------+
    
    
    
    
    
    
    
    
    
    
    
    
    Scala版本:
    package com.spark.spark_sql
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    
    object DataFrameCreate {
      def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setAppName("DataFrameCreate")
        val sc = new SparkContext(conf)
        val sqlContext = new SQLContext(sc)
        val df = sqlContext.read.json("hdfs://hadoop01:8020/spark_input/student.json")
        df.show
      }
    }
    
    
    Dataframe常用的操作
    java
    package com.spark.spark_sql;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.SQLContext;
    
    /**
     * Dataframe的常用的操作
     * @author Administrator
     *
     */
    public class DataFrameOperation {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setAppName("DataFrameOperation");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext SQLContext = new SQLContext(sc);
            //创建出来的dataframe完全可以可以理解为一张表
            DataFrame df = SQLContext.read().json("hdfs://hadoop01:8020/input/students.txt");
            //打印dataframe中所有的数据
            df.show();
            //打印dataframe中元数据的信息
            df.printSchema();
            //查询某列所有的数据
            df.select("name").show();
            //查询某几列所有的数据,并对列进行计算
            df.select(df.col("name"), df.col("age").plus(1)).show();//将查询出来的age进行加一
            //根据某一列的值进行过滤
            df.filter(df.col("age").gt(30)).show();                //年龄大于30的进行过滤
            //根据某一列进行分组聚合
            df.groupBy(df.col("age")).count().show();
        }
    }
    
    Scala
    package com.spark.spark_sql
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    
    object DataframeOperation {
      def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setAppName("DataframeOperation")
        val sc = new SparkContext(conf)
        val SQLContext = new SQLContext(sc)
        val df = SQLContext.read.json("hdfs://hadoop01:8020/spark_input/student.json" 
        df.show
        df.printSchema()
        df.select("name").show
        df.select(df("name"),df("age")+1).show
        df.filter(df("age") > 30).show
        df.groupBy("age").count().show
     }
    }
    
    
    5.RDD与Dataframe之间转换
    为什么要将RDD转换为Dataframe?因为这样的话,我们就可以直接针对hdfs上的任何可以构建为RDD的数据,使用spark sql 进行sql查询,这个功能无比强大。想象一下,针对hdfs中的数据,直接就可以使用sql进行查询
    
    Spark sql 支持两种方式来将RDD转换为Dataframe
    
    第一种方式,是使用反射来推断包含了特定数据类型的RDD的元数据,这种基于反射的方式,代码比较简单,当你已经知道你的RDD的元数据时,是一种非常不错的方式。
    
    第二种方式,是通过编程接口来创建dataframe,你可以在程序运行的时候动态构建一份元数据,然后将其应用到已经存在的RDD上,这种方式的代码比较冗长,但是如果在编写程序时,还不知道RDD的元数据,只有在程序运行时,才能动态得知元数据,那么只能通过这种动态构建元数据的方式。
    
    
    1.使用反射的方式推断元数据
    Java版本:spark sql是支持将包含javaBean的RDD转换为dataframe的,Javabean的信息,就定义了元数据。Spark sql 现在是不支持包含嵌套javabean或者list等复杂元数据的Javabean。
    
    Scala版本:而scala由于具有隐式转换的特性,所以spark sql的scala接口,是支持自动将包含case class 的RDD转换为Dataframe的。Case class 就定义了元数据。Spark sql 会通过反射读取传递给 case class 的参数的名称,然后将其作为列名。与java不同的是,Spark sql是支持将包含了嵌套的数据结构的case class作为元数据的,比如包含了Array等。
    Java版本:
    package com.spark.spark_sql;
    import java.io.Serializable;
    public class Student implements Serializable {
        private static final long serialVersionUID = 1L;
        private int id;
        private String name;
        private int age;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public Student() {
        }
        public Student(int id, String name, int age) {
            super();
            this.id = id;
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
        }
    
    }
    
    
    package com.spark.spark_sql;
    
    import java.util.List;
    
    import org.apache.spark.SparkConf;
    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.sql.DataFrame;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.SQLContext;
    
    /**
     * 使用反射的方式将RDD转换为dataframe
     * @author Administrator
     *
     */
    public class RDD2DataframeReflection {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setMaster("local").setAppName("RDD2DataframeReflection");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext SQLContext = new SQLContext(sc);
            JavaRDD<String> lines = sc.textFile("E://student.txt");
            JavaRDD<Student> students = lines.map(new Function<String, Student>() {
                private static final long serialVersionUID = 1L;
    
                @Override
                public Student call(String line) throws Exception {
                    String[] split = line.split(",");
                    Student stu = new Student();
                    stu.setId(Integer.parseInt(split[0]));
                    stu.setName(split[1]);
                    stu.setAge(Integer.parseInt(split[2]));
                    return stu;
                }
            });
            //使用反射的方式将RDD转换为dataframe
            
            //将student.class传入进去其实就是通过反射的方式来创建dataframe
            //因为student.class 本身就是反射的一个应用
            //然后底层还得通过student class 进行反射。来获取其中的fields
            //这里要求Javabean要实现Serializable接口,可以序列化
    DataFrame studentDF = sqlContext.createDataFrame(students, Student.class);
            
            //拿到一个dataframe之后,就可以将其注册为一张临时表,然后针对其中的数据进行sql语句
            studentDF.registerTempTable("student");
            
            //针对student临时表执行sql语句,查询年龄大于20岁的学生
            DataFrame df =sqlContext.sql("select * from student where age > 20");
            
            //将查询出来的dataframe再次转换为RDD
            JavaRDD<Row> teenagerRDD = df.javaRDD();
            
            //将RDD中的数据,进行映射,给每个人的年龄,然后映射为student
            JavaRDD<Student> teenagerStudentRDD = teenagerRDD.map(new Function<Row, Student>() {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public Student call(Row row) throws Exception {
                    //row中的顺序是按照字典顺序进行排列的
                    Student student = new Student();
                    student.setAge(row.getInt(0));
                    student.setId(row.getInt(1));
                    student.setName(row.getString(2));
                    return student;
                }
            });
            
            //将数据collect 回来,打印出来
            List<Student> studentList = teenagerStudentRDD.collect();
            for (Student student : studentList) {
                System.out.println(student);
            }
            
        }
    
    }
    
    Scala版本:
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    /**
     * 如果要使用scala开发spark程序
     * 然后在其中还要实现基于反射的RDD到dataframe的转换,就必须得用object  extends App的方式
     * 不能使用def main()方法的方式来运行程序,否则就会报错no typetag for ... class
     */
    object RDD2DaframeReflection extends App{
      
        val conf = new SparkConf().setMaster("local").setAppName("RDD2DaframeReflection")
        val sc = new SparkContext(conf)
        val sqlContext = new SQLContext(sc)
        val lines = sc.textFile("e://student.txt", 1)
        
        //在scala中使用反射的方式,进行RDD到Dataframe的转换,需要手动的导入一个隐士转换
        import SQLContext.implicits._
        case class Student(id : Int ,name : String ,age : Int)
        
        //这里其实就是一个普通的,元素是case class 的rdd
        val students = lines.map(line =>line.split(",")).map(arr => Student(arr(0).trim().toInt,arr(1),arr(2).trim().toInt))
        
        //直接使用RDD的toDF,即可将其转换为dataframe
        val studentDF=students.toDF()
        
        //注册为一个临时表
        studentDF.registerTempTable("student")
        
        //
        val teenagerDF = sqlContext.sql("select * from student where age > 20")
        
        val teenagerRDD = teenagerDF.rdd
        
        //在scala中,row中的数据的顺序,反而是按照我们期望的来排列的,这个是跟java是不一样的
        teenagerRDD.map{row => Student(row(0).toString().toInt,row(1).toString(),row(2).toString().toInt )
        
        }.collect().foreach(stu => print(stu.id+ ":" +stu.name+":"+stu.age))
        
        
        //在scala中,对row的使用比java中的row更加的丰富
        //在scala中,可以用row的getAs()方法,获取指定列名的列
        teenagerRDD.map(row =>Student(row.getAs[Int]("id"),row.getAs("name"),row.getAs("age"))).collect()
          .foreach(stu => print(stu.id+ ":" +stu.name+":"+stu.age))
        
          //还可以通过row的getValuesMap,方法,获取指定几列的值,返回的是一个map
        teenagerRDD.map(row => {
          val map = row.getValuesMap(Array("id","name","age"))
          Student(map("id").toString().toInt,map("name").toString(),map("age").toString().toInt)
        }).collect().foreach(stu => print(stu.id+ ":" +stu.name+":"+stu.age))
    }
    
    
    
    
    2.通过编程接口来创建dataframe
    Java版本:
    package com.spark.spark_sql;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.spark.SparkConf;
    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.sql.DataFrame;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.RowFactory;
    import org.apache.spark.sql.SQLContext;
    import org.apache.spark.sql.types.DataType;
    import org.apache.spark.sql.types.DataTypes;
    import org.apache.spark.sql.types.StructField;
    import org.apache.spark.sql.types.StructType;
    
    /**
     * 以编程的方式动态的执行元数据,将RDD转化为dataframe
     * @author Administrator
     *
     */
    public class RDD2DataProgrammatically {
    public static void main(String[] args) {
        
        //创建sparkconf
        SparkConf conf = new SparkConf().setMaster("local").setAppName("RDD2DataProgrammatically");
        JavaSparkContext sc = new JavaSparkContext(conf);
        SQLContext sqlContext = new SQLContext(sc);
        
        //第一步,创建一个普通的RDD,但是必须将其转换为RDD<Row>的格式
            JavaRDD<String> lines = sc.textFile("e://student.txt");
            
            JavaRDD<Row> rows = lines.map(new Function<String, Row>() {
                private static final long serialVersionUID = 1L;
    
                @Override
                public Row call(String line) throws Exception {
                    String[] split = line.split(",");
                    
                    return RowFactory.create(Integer.parseInt(split[0]),split[1],Integer.parseInt(split[2]));
                }
            });
            //第二步,动态元数构造据
            //比如说,id name age 等fields的名称和类型都是在程序运行过程中,
            //动态的从MySQL等DB中或者是配置文件中,加载出来的,是不固定的
            //所以特别适合这种编程的方式来构造元数据
            
            List<StructField> fields = new ArrayList<StructField>();
            
            fields.add(DataTypes.createStructField("id", DataTypes.IntegerType, true));
            fields.add(DataTypes.createStructField("name", DataTypes.StringType, true));
            fields.add(DataTypes.createStructField("age", DataTypes.IntegerType, true));
            
            StructType structType = DataTypes.createStructType(fields);
            
            //第三部,使用动态构造元数据,将RDD转换为dataframe
            DataFrame studentDF = sqlContext.createDataFrame(rows, structType);
            
            //后面就可以直接使用这个df了
            //注册临时表
            studentDF.registerTempTable("student");
            DataFrame stus = sqlContext.sql("select * from student where age > 20");
            
            List<Row> list = stus.javaRDD().collect();
            
            for (Row row : list) {
                System.out.println(row);
            }    
    }
    }
    Scala版本:
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.types.StructType
    import org.apache.spark.sql.types.StructField
    import org.apache.spark.sql.types.IntegerType
    import org.apache.spark.sql.types.StringType
    
    object RDD2DataframeProgramatically extends App{
     val conf = new SparkConf().setMaster("local").setAppName("RDD2DataframeProgramatically");
      
      val sc = new SparkContext(conf)
      
      val sqlContext = new SQLContext(sc)
      
      //第一步:构造出元素为Row的普通的RDD
      val studentRDD = sc.textFile("e://student.txt", 1)
          .map(line => Row(
                line.split(",")(0).toInt,
                line.split(",")(1),
                line.split(",")(2).toInt))
      
       //第二步:以编程的方式构造元数据
       val structType =StructType(Array(
           StructField("id",IntegerType,true),
           StructField("name",StringType,true),
           StructField("age",IntegerType,true)))
      
      //第三步:进行RDD到dataframe的转换
           
       val studentDF = SQLContext.createDataFrame(studentRDD,structType )  
       
       studentDF.registerTempTable("student");
      
      val teenagerDF = sqlContext.sql("select * from student where age > 20")
      
       teenagerDF.rdd.collect.foreach(row => println(row))
      
    
      
    }
    
    
    6.通用的load和save操作
    1.dataframe的load和save
    
    对于的spark sql的dataframe来说,无论是从什么数据源创建出来的dataframe,都有一些共同的load和save操作,load操作主要是用于加载数据,创建出来的dataframe:save操作,主要用于将dataframe中的数据集保存到文件中(保存到的是一个目录中)
    
    Java版本:
    Dataframe df = sqlContext.read().load(“users.parquet”);
    df.select(“name”,”facourite_color”).write().save(“nameAndFav_color_dir”);
    
    
    Scala版本:
    Val df = sqlContext.read.load(“users.parquet”)
    Df.select(“name”,”facourite_color”).write().save(“nameAndFav_color_dir”)
    
    
    Java版本:
    package com.spark.spark_sql;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.SQLContext;
    /**
     * 通用的load和save操作
     * @author Administrator
     */
    public class GenericLoadSave {
        public static void main(String[] args) {
            
        SparkConf conf = new SparkConf().setMaster("local").setAppName("GenericLoadSave");
        JavaSparkContext sc = new JavaSparkContext(conf);
        SQLContext sqlContext = new SQLContext(sc);
        DataFrame usersDF = SQLContext.read().load("C://Users//Administrator//Desktop//users.parquet");
        
        usersDF.printSchema();
        usersDF.show();
        usersDF.select("name","favourite_color").write().save("e://users2");
        }
    }
    
    Scala版本:
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import scala.tools.scalap.Main
    import org.apache.spark.sql.DataFrame
    
    object GenericLoadSave {
      def main(args: Array[String]): Unit = {
    
        val conf = new SparkConf().setAppName("GenericLoadSave")
        val sc = new SparkContext(conf)
        val sqlContext = new SQLContext(sc)
        val  usersDF = sqlContext.read.load("hdfs://hadoop01:8020/spark_input/users.parquet")
        usersDF.select("name","favourite_color").write.save("hdfs://hadoop01:8020/spark_output")
    
      }
    }
    
    2.手动指定数据源:
    
    也可以手动指定用来操作的数据源,数据源通常是使用其权限定名来指定,比如parquet是org.apache.spark.sql.parquet。但是spark sql内置了一些数据源类型,比如json,parquet.jdbc等等,实际上,通过这个功能,就可以在不同类型的数据源之间进行转换了。比如将json文件中的数据存储到parquet文件中。默认情况下,如果不指定数据源,默认就是parquet
    Java版本:
    DataFrame df =SQLContext.read().format(“json”).load(“people,json”)
    Df.select(“name”,”age”).write().format(“parquet”).save(“out”)
    
    Scala版本:
    Val df = SQLContext.read.format(“json”).load(“people,json”)
    Df.select(“name”,”age”).write.format(“parquet”).save(“out”) 
    
    
    Java版本
         
    package com.spark.spark_sql;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.SparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.SQLContext;
    
    /**
     * 手动指定数据源
     * @author Administrator
     */
    public class ManuallySpecifyOptions {
        public static void main(String[] args) {
            
            SparkConf conf = new SparkConf().setMaster("local").setAppName("ManuallySpecifyOptions");
            
            SparkContext sc = new SparkContext(conf);
            
            SQLContext SQLContext = new SQLContext(sc);
            
            DataFrame df = SQLContext.read().format("json").load("e://users.parquet");
            
            df.write().format("json").save("e://out");
            
        }
    }
    
    
    3.Save mode 
    
    Spark sql 对于save操作,提供了不同的save mode.主要用来处理,当目标位置已经有数据时,应该如何处理。而且save操作并不会执行锁操作,并且不是原子的,因此是有一定风险出现脏数据的
    
    Save mode    意义
    SaveMode.ErrorIfExists(默认)    如果目标位置存在数据,那么就抛出异常
    SaveMode.Append    如果目标位置存在数据,那么就将数据追加进去
    SaveMode.Overwrite    如果目标位置存在数据,那么就将已经存在的数据删除,用新的数据进行覆盖
    SaveMode.Ignore    如果目标位置存在数据,那么就忽略,不做任何的操作
    Java版本:
    package com.spark.spark_sql;
    import org.apache.derby.impl.tools.sysinfo.Main;
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.SQLContext;
    import org.apache.spark.sql.SaveMode;
    /**
     * savemode 示例
     * @author Administrator
     */
    public class SaveModeDemo {
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setMaster("local").setAppName("SaveModeDemo");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext sqlContext = new SQLContext(sc);
            DataFrame df = sqlContext.read().format("json").load("e://users.parquet");
            df.save("e://out", SaveMode.Append);
    
        }
    }
    
    三、Parquet数据源
    1.使用编程方式加载数据
    Parquet是面向分析型业务的列式存储格式,由Twitter和cloudera合作开发。2015年成为Apache的顶级项目
    
    列式存储和行式存储相比较有哪些优点;
    1.可以跳过不符合条件的数据,只读取需要的数据,降低IO数据量
    2.压缩编码可以降低磁盘存储空间。由于同一列的数据类型是一样的,可以使用更高效的压缩编码(例如Run Length Encoding 和 Delta Encoding)进一步节约磁盘空间。
    3.只读取需要的列,支持向量运算,能够获取更好的扫描性能
    Java版本:
    
    package com.spark.spark_sql;
    
    import java.util.List;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.api.java.function.Function;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.SQLContext;
    
    /**
     * parquet数据源之使用编程方式加载数据
     */
    public class ParquetLoadData {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setMaster("local").setAppName("ParquetLoadData");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext sqlContext = new SQLContext(sc);
            //读取parquet文件中的数据,创建一个dataframe
            DataFrame userDF = sqlContext.read().parquet("e://users.parquet");
            
            //将其注册为临时表,使用sql查询所需要的数据
            userDF.registerTempTable("users");
            
            DataFrame userNamesDF = sqlContext.sql("select name from users");
            
            //对查询出来的dataframe进行transformation操作,处理数据,然后打印出来
            List<String> userNames = userNamesDF.javaRDD().map(new Function<Row, String>() {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public String call(Row row) throws Exception {
                    
                    return "Name: "+row.getString(0);
                }
            }).collect();
            
            for (String name : userNames) {
                System.out.println(name);
            }
        }
    }
    
    
    
    Scala版本:
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    
    object PatquetLoadData {
      
      val conf = new SparkConf().setAppName("PatquetLoadData")
      val sc = new SparkContext(conf)
      val sqlContext =new SQLContext(sc)
      val usersDF = sqlContext.read.parquet("hdfs://hadoop01:8020/spark_input/users.parquet");
      usersDF.registerTempTable("user");
      val userNameDF = sqlContext.sql("select * from user")
      userNameDF.rdd.map(row => "Name: "+row(0)).collect.foreach(userName =>println(userName))
    }
    2.自动分区推断
    表分区是一张常见的优化的方式,比如hive中就提供分区表的特性。在一个分区表中,不同分区的数据通常存储在不同的目录中,分区列的值通常就包含在了分区的目录名中。Spark sql中的parquet数据源,支持自动根据目录名推断出分区信息。例如,如果将入口数据存储在分区表中,并且使用性别和国家作为分区列。那么目录结构可能是如下所示:
    
    |----tableName
    |----gender=male
      |----country=US
              |.....
      |----country=ZH
    |----gender=female
      |--country=...
               |...
    如果将tableName传入SQLContext.read.parquet()或者SQLContext.read.load()方法,那么sparksql就会自动根据目录的结构,推断出分区的信息,是gender和country。即使数据文件中包含两列的值name和age,但是sparksql返回的dataframe,调用printSchema()方法时,会打印出四个列的值:name age country gender .这就是自动分区推断的功能
    
    此外,分区的列的数据类型,也是自动被推断出来的。目前,spark sql仅支持自动推断出数字类型和字符串类型。有时,用户也许不希望spark sql自动推断分区列的数据类型。此时只要设置一个配置即可,spark.sql.sources.partitionColumnTypeInference.enabled,默认是true,即自动推断分区列的类型,设置为false,即不会自动推断类型。进行自定推断分区列的类型时,所有的分区列的类型,就统一默认的是String
    
    案列:
    1.hdfs上创建相对应的目录结构:
    bin/hdfs dfs -mkdir -p /spark_input/gender=male/country=US
    2.将文件上传到目录下
    bin/hdfs dfs -put users.parquet /spark_input/gender=male/country=US/
    
    3.查询出schema
    package com.spark.spark_sql;
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.SQLContext;
    /**
     *parquet数据源之 自动推断分区
     */
    public class ParquetPartitionDiscovery {
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setAppName("ParquetPartitionDiscovery").setMaster("local");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext sqlContext = new SQLContext(sc);
            DataFrame userDF = sqlContext.read().parquet("hdfs://hadoop01:8020/spark_input/gender=male/country=US/users.parquet");
            userDF.printSchema();
             userDF.show();
        }
    }
    
    
    4.结果:
    root
     |-- name: binary (nullable = true)
     |-- favourite_color: binary (nullable = true)
     |-- gender: string (nullable = true)
     |-- country: string (nullable = true)
    
    +----------------+-------------------+------+-------+
    |            name|    favourite_color|gender|country|
    +----------------+-------------------+------+-------+
    |      [6C 65 6F]|         [72 65 64]|  male|     US|
    |   [6A 61 63 6B]|[79 65 6C 6C 6F 77]|  male|     US|
    |[6B 69 74 74 79]|   [77 68 69 74 65]|  male|     US|
    |      [74 6F 6D]|   [67 72 65 65 6E]|  male|     US|
    |      [61 6C 6C]|      [70 69 6E 6B]|  male|     US|
    +----------------+-------------------+------+-------+
    【注意】前面是因为parquet数据的问题,自己从hive中导出来的。可以利用json格式的数据load,然后save的时候以parquet的方式生成一个parquet文件,用于测试
    
    3.合并元数据
    如同ProtocolBuffer,Avro,Thrift一样,Parquet也是支持元数据合并的。用户可以一开始就定义一个简单的元数据,然后随着业务需要。逐渐往元数据中添加更多的列,在这种情况下,用户可能会创建多个parquet文件,有着多个不同的却互相兼容的元数据。Parquet数据源支持自动推断这种情况,并且进行多个parquet文件的元数据的合并
    
    因为元数据合并是一种相对耗时的操作,而且在大多数的情况下不是一种必要的特性,从spark1.5.0版本开始,默认是关闭parquet文件的自动合并元数据的。可以通过以下的两种方式开启parquet数据源的自动合并元数据的特性
    
    1.读取parquet文件时,将数据源的选项,mergeSchema,设置为true
    
    2.根据sqlContext.setConf()方法,将spark.paquet.mergeSchema参数设置为true
    
    案例:
    合并学生的基本信息和成绩信息的元数据
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import org.apache.spark.sql.SaveMode
    
    object ParquetMergeSchema {
      def main(args: Array[String]): Unit = {
        
     
      val conf = new SparkConf().setAppName("ParquetMergeSchema")
      val sc = new SparkContext(conf)
      val sqlContext = new SQLContext(sc)
      
      import SQLContext.implicits._
      //创建一个dataframe,作为学生的基本信息,并写入一个parquet文件中
      val studentsWithNameAge =Array(("leo",23),("jack",25))
      
      val studentsWithNameAgeDF = sc.parallelize(studentsWithNameAge, 2).toDF("name","age")
      studentsWithNameAgeDF.save("hdfs://hadoop01:8020/spark_out/students","parquet", SaveMode.Append)
      
      //创建一个dataframe,作为学生的成绩信息,并写入一个parquet文件中
       val studentsWithNameGrade =Array(("marry","A"),("jack","B"))
      
      val studentsWithNameGradeDF = sc.parallelize(studentsWithNameGrade, 2).toDF("name","age")
      studentsWithNameGradeDF.save("hdfs://hadoop01:8020/spark_out/students","parquet", SaveMode.Append)
      
      
      //首先,第一个dataframe和第二个dataframe的元数据肯定是不一样的
      //一个包含了name和age两个列,一个是包含name和grade两个列
      //所以,这期望的是,读取出来的表数据,自动合并两个文件的元数据,出现三列,name age grade 
      
      
      //用mergeSchema的方式,读取students表中的数据,进行元数据的合并
      val studentsDF = sqlContext.read.option("mergeSchema", "true").parquet("hdfs://hadoop01:8020/spark_out/students")
      
      studentsDF.printSchema();
      studentsDF.show();
      
      } 
    }
    四、JSON数据源
    Spark sql 可以自动推断JSON文件的元数据,并且加载其数据,创建一个dataframe。可以使用SQLContext.read.json()方法,针对一个元素为String的RDD,或者是一个JSON文件
    
    但是要注意的是,这里使用的JSON文件与传统意义上的JSON文件是不一样的。每行都必须也只能包含一个,单独的,自包含的,有效的JSON对象。不能让json对象分散在多行。否则会报错
    
    综合性复杂案例:查询成绩为80分以上的学生的基本信息与成绩信息
    Java 版本
    package com.spark.spark_sql;
    
    import java.util.ArrayList;
    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 org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.RowFactory;
    import org.apache.spark.sql.SQLContext;
    import org.apache.spark.sql.SaveMode;
    import org.apache.spark.sql.types.DataTypes;
    import org.apache.spark.sql.types.StructField;
    import org.apache.spark.sql.types.StructType;
    
    import scala.Tuple2;
    /**
     * json数据源
     */
    public class JSONDataSource {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setAppName("JSONDataSource").setMaster("local");
            JavaSparkContext sc = new JavaSparkContext(conf);
            SQLContext sqlContext = new SQLContext(sc);
    
            // 针对json文件,创建出dataframe
            DataFrame studentDF = sqlContext.read().json("e://student.json");    //hdfs://hadoop01:8020/spark_input/student.json
            // 针对学生成绩信息的dataframe,注册临时表,查询分数大于80分的学生的姓名和分数
            studentDF.registerTempTable("student");
    
            DataFrame stuNameDF = sqlContext.sql("select name,score from student where score > 80");
    
            List<String> goodName = stuNameDF.javaRDD().map(new Function<Row, String>() {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public String call(Row row) throws Exception {
    
                    return row.getString(0);
                }
            }).collect();
    
            // 针对JavaRDD<String> 创建dataframe
            List<String> studentInfoJSONs = new ArrayList<String>();
            studentInfoJSONs.add("{"name":"leo","age":18}");
            studentInfoJSONs.add("{"name":"marry","age":15}");
            studentInfoJSONs.add("{"name":"jack","age":30}");
            JavaRDD<String> studentInfoJSONsRDD = sc.parallelize(studentInfoJSONs);
    
            DataFrame studentInfoDF = sqlContext.read().json(studentInfoJSONsRDD);
    
            // 针对学生的基本信息的dataframe,注册临时表,然后查询分数大于80分的学生的基本信息
    
            studentInfoDF.registerTempTable("info");
            String sql = "select name , age  from  info where name in (";
    
            for (int i = 0; i < goodName.size(); i++) {
                sql += "'" + goodName.get(i) + "'";
                if (i < goodName.size() - 1) {
                    sql += ",";
                }
            }
            sql += ")";
            DataFrame goodStuInfosDF = sqlContext.sql(sql);
    
            // 将两份数据的dataframe转换为javapairRDD,执行join transformation
    
            JavaPairRDD<String, Tuple2<Integer, Integer>> pairs = goodStuInfosDF.javaRDD()
                    .mapToPair(new PairFunction<Row, String, Integer>() {
    
                        private static final long serialVersionUID = 1L;
    
                        @Override
                        public Tuple2<String, Integer> call(Row row) throws Exception {
    
                            return new Tuple2<String, Integer>(row.getString(0), Integer.valueOf((int) row.getLong(1)));
                        }
                    }).join(stuNameDF.javaRDD().mapToPair(new PairFunction<Row, String, Integer>() {
    
                        private static final long serialVersionUID = 1L;
    
                        @Override
                        public Tuple2<String, Integer> call(Row row) throws Exception {
                            return new Tuple2<String, Integer>(row.getString(0), Integer.valueOf((int) row.getLong(1)));
                        }
                    }));
            
            //将封装在RDD中好学生的全部信息,转换为一个javaRDD<Row>的格式
            JavaRDD<Row> rows = pairs.map(new Function<Tuple2<String,Tuple2<Integer,Integer>>, Row>() {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public Row call(Tuple2<String, Tuple2<Integer, Integer>> t) throws Exception {
                    
                    return RowFactory.create(t._1,t._2._2,t._2._1);
                }
            });
            
            //创建一份元数据,将JavaRDD<Row>转换为dataframe
            List<StructField> fields = new ArrayList<StructField>();
            fields.add(DataTypes.createStructField("name", DataTypes.StringType, true));
            fields.add(DataTypes.createStructField("score", DataTypes.IntegerType, true));
            fields.add(DataTypes.createStructField("age", DataTypes.IntegerType, true));
            StructType structType = DataTypes.createStructType(fields);
            DataFrame goodStudentsDF =sqlContext.createDataFrame(rows, structType);
                    
            //将好学生的全部信息保存到json文件中去
            goodStudentsDF.write().format("json").save("e://good");    //hdfs://hadoop01:8020/spark_out/goodStudent
            
        }
    }
    
     
    Scala版本:
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.types.StructType
    import org.apache.spark.sql.types.StructField
    import org.apache.spark.sql.types.StringType
    import org.apache.spark.sql.types.IntegerType
    
    object JSONDatasource {
      val conf = new SparkConf().setAppName("JSONDatasource")
      val sc = new SparkContext(conf)
      val sqlContext = new SQLContext(sc)
      val studentScoreDF =sqlContext.read.json("hdfs://hadoop01:8020/spark_input/student.json")
      studentScoreDF.registerTempTable("stu_score")
      val goodStuScoreDF = sqlContext.sql("select name , score from stu_score where score > 80")
      
      val goodStuNames = goodStuScoreDF.map(row => row(0)).collect()
      
      
      val studentInfoJSON =Array("{"name":"Leo","age":18}","{"name":"marry","age":15}","{"name":"jack","age":30}") 
      
      
      val studentInfoRDD =sc.parallelize(studentInfoJSON, 3)
      
      val studentInfoDF = sqlContext.read.json(studentInfoRDD)//json格式特有的,可以接受RDD
      
      studentInfoDF.registerTempTable("stu_info")
      
      var sql = "select name , age from stu_info where name in ("
      
      for(i <- 0 until goodStuNames.length){
        
        sql += "'"+goodStuNames(i)+"'"
        if(i < goodStuNames.length-1){
          sql += ","
        }
        
      }
      sql += ")"
      
      val goodStuInfoDF = sqlContext.sql(sql)
      
      val goodStuRDD = goodStuInfoDF.rdd.map{row => (row.getAs[String]("name"),row.getAs[Long]("age"))}.join (goodStuScoreDF.rdd.map{row => (row.getAs[String]("name"),row.getAs[Long]("score"))})
      
      val goodStuRows = goodStuRDD.map(info => Row(info._1,info._2._2.toInt,info._2._1.toInt))
      
      val structType = StructType(Array(StructField("name",StringType,true),StructField("score",IntegerType,true),StructField("age",IntegerType,true)))
      
      val goodStudentDF = sqlContext.createDataFrame(goodStuRows, structType)
      
      goodStudentDF.write.format("json").save("hdfs://hadoop01:8020/spark_out/good_scala")
      
      
    }
    
    
    五、Hive数据源
    Spark sql 支持对hive中存储的数据进行读写,操作hive中的数据时,就必须创建HiveContext,而不是SQLContext。HiveContext继承SQLContext,但是增加了在hive元数据库中查找表,以及用hiveql语法编写SQL的功能。处理sql()方法外,HiveContext还提供hql()方法,从而用hive语法来编译sql。
    
    使用HiveContext,可以执行Hive的大部分功能,包括创建表、往表里导入数据以及用SQL语句查询表中的数据,查询出来的数据是一个row数组
    
    将hive-site.xml拷贝到spark/conf目录下,将mysql connector拷贝到spark/lib目录下
    
    HiveContext sqlContext = new HiveContext(sc);
    sqlContext.sql(“create table if not exists student (name string ,age int)”)
    sqlContext.sql(“load data local inpath ‘/usr/local/student.txt’into table students”);
    Row[] teenagers = sqlContext.sql(“select name ,age from students where age <= 18”).collect();
    
    
    将数据保存到表中
    Spark sql还允许将数据保存到hive表中。调用Dataframe的saveAsTable,即可将dataframe中的数据保存到hive表中。与registerTempTable不同,saveAsTable是会将dataframe汇总的数据物化到hive表中,而且还会在hive元数据库中创建表的元数据
    
    默认情况下,saveAsTable会创建一张hive managed table,也就是说,数据的位置都是有元数据库中的信息控制的。当managed table 被删除时,表中的数据也会一并内物理删除
    
    RegisterTempTable只是注册一个临时的表,只要spark application重启或者停止了,那么表就没有了。而SaveAsTable创建的是物化的表,无论是spark application重启还是停止,表都会一直存在
    
    调用HiveContext.table()方法,还可以直接针对hive中的表,创建一个dataframe
    
    案例:查询分数大于80分的学生的信息
    package com.spark.spark_sql;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.hive.HiveContext;
    /**
     * hive数据源
     * @author Administrator
     */
    public class HiveDataSource {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setAppName("HiveDataSource");
            JavaSparkContext sc = new JavaSparkContext(conf);
            //创建HiveContext,注意,这里接收的是sparkContext作为参数,不是Javasparkcontext
            HiveContext hiveContext = new HiveContext(sc.sc());
            
            //第一个功能,使用HiveContext的sql()/hql()方法,可以执行hive中可以执行的hiveQL语句
            //判断是否存在student_info表,如果存在则删除
            hiveContext.sql("drop  table if exists student_info");
            //判断student_info表是否不存在,不存在就创建该表
            hiveContext.sql("create  table if not exists student_info(name string,age int) row format delimited fields terminated by '	'");
            
            //将学生基本信息导入到Student_info表
            hiveContext.sql("load data local inpath '/home/hadoop/student_info.txt' into table student_info");
            //用同样的方式向student_scores导入数据
            hiveContext.sql("drop  table if exists student_score");
            
            hiveContext.sql("create  table if not exists student_score(name string,score int)row format delimited fields terminated by '	'");
            
            hiveContext.sql("load data local inpath '/home/hadoop/student_score.txt' into table student_score");
            
            //第二个功能个,执行sql还可以返回dataframe,用于查询
            
            //执行sql查询,关联两种表,查询成绩大于80 分的学生信息
            DataFrame goodstudentDF  = hiveContext.sql("select i.name,i.age,s.score from student_info i join student_score s on i.name=s.name where s.score >=80");
            
            //第三个功能,可以将dataframe中的数据,理论上来说,dataframe对应的RDD的元素是ROW就可以
            //将Dataframe中的数据保存到hive表中
            
            //将dataframe中的数据保存到good_student_info表中
            hiveContext.sql("drop  table if exists good_student_info");
            goodstudentDF.saveAsTable("good_student_info");
            
            //第四个功能,可以使用table()方法,针对hive表直接创建dataframe
            
            //针对good_student_info表,直接创建dataframe
            Row[] rows = hiveContext.table("good_student_info").collect();
            for (Row row : rows) {
                System.out.println(row);
            }
            
            sc.close();
        }
    }
    
    
    
    Scala版本:
    package com.spark.spark_sql
    import org.apache.spark.SparkConf
    import org.apache.spark.sql.hive.HiveContext
    import org.apache.spark.sql.DataFrame
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.DataFrame
    import org.apache.spark.SparkContext
    
    object HiveDataSource {
      def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setAppName("HiveDataSource");
            val sc = new SparkContext(conf)
            //创建HiveContext,注意,这里接收的是sparkContext作为参数,不是Javasparkcontext
            val hiveContext = new HiveContext(sc);
            
            //第一个功能,使用HiveContext的sql()/hql()方法,可以执行hive中可以执行的hiveQL语句
            //判断是否存在student_info表,如果存在则删除
            hiveContext.sql("drop  table if exists student_info");
            //判断student_info表是否不存在,不存在就创建该表
            hiveContext.sql("create  table if not exists student_info(name string,age int) row format delimited fields terminated by '	'");
            
            //将学生基本信息导入到Student_info表
            hiveContext.sql("load data local inpath '/home/hadoop/student_info.txt' into table student_info");
            
            //用同样的方式向student_scores导入数据
            hiveContext.sql("drop  table if exists student_score");
            
            hiveContext.sql("create  table if not exists student_score(name string,score int)row format delimited fields terminated by '	'");
            
            hiveContext.sql("load data local inpath '/home/hadoop/student_score.txt' into table student_score");
            
            //第二个功能个,执行sql还可以返回dataframe,用于查询
            
            //执行sql查询,关联两种表,查询成绩大于80 分的学生信息
            val goodstudentDF  = hiveContext.sql("select i.name,i.age,s.score from student_info i join student_score s on i.name=s.name where s.score >=80");
            
            //第三个功能,可以将dataframe中的数据,理论上来说,dataframe对应的RDD的元素是ROW就可以
            //将Dataframe中的数据保存到hive表中
            
            //将dataframe中的数据保存到good_student_info表中
            hiveContext.sql("drop  table if exists good_student_info");
            goodstudentDF.saveAsTable("good_student_info");
            
            //第四个功能,可以使用table()方法,针对hive表直接创建dataframe
            
            //针对good_student_info表,直接创建dataframe
            val rows = hiveContext.table("good_student_info").collect();
            for ( row <- rows) {
                println(row);
            }
      }
    }
    
    六、内置函数
    在spark 1.5.x版本,增加了一系列内置函数到dataframe API中,并且实现了code-generation的优化。与普通的函数不同,dataframe的函数并不会执行后立即返回一个结果值,而是返回一个Column对象,用于在并行作业中进行求值。Column可以用在dataframe的操作之中,比如select, filter,groupBy等操作。函数的输入值,也可以是Column。
    
    种类    函数
    聚合函数    approxCountDistinct,avg,count,
    countDistinct,first,last,max,mean,min,sum,sumDistinct
    集合函数    Array_contains,explode,size,sort_array
    日期/时间函数    日期时间转换
    Unix_timestamp,from_unixtime,to_date
    Quarter,day,dayofyear,weekofyear,
    From_utc_timestamp,to_utc_timestamp
    从日期中提取字段
    Year,month,dayofmonth,hour,minute,
    second
    日期/时间函数    日期时间计算
    Dateiff,date_add,date_sub,add_months,
    last_day,next_day,months_between
    获取当前时间
    Current_date,current_timestamp,trunc,
    date_format
    数学函数    Abs,scros,asin,atan,atan2,bin,cbrt,
    ceil,conv,cos,sosh,exp,expm1,factorial,floor,hex,hypot,log,log10,log1p,log2,
    Pmod,pow,rint,round,shiftLeft,
    shiftRight,shiftRightUnsigned,sifnum,
    Sin,sinh,sqrt,tan,tanh,toDegrees,
    toRadians,unhex
    混合函数    Array,bitwiseNOT,callUDF,coalesce,
    crc32,greatest,if,inputFileName,isNaN,
    isnotnull,isnull,least,lit,md5,
    monotonicalliIncreasingId,nanvl,negate,not,rand,randn,randn
    sha,sha1,sparkPartitionId,struct,when
    字符串函数    Ascii,base64,concat,concat_ws,decode,encode,format_number,format_string,get_json_object,initcap,instr,length,levenshtein,locate,lower,lpad,ltrim,printf,redexp_extract,regexp_replace,repeat,reverse,rpad,rtrim,soundex,space,split,substring,substring_index,translate,trim,unbase64,upper
    窗口函数     cumeDist,denseRank,lag,lead,ntile,percentRank,rank,rowNumber
    
    案例:
    根据每天的用户访问和购买日志,统计每日的UV和销售额
    
    统计每日的UV
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.types.StructType
    import org.apache.spark.sql.types.StructField
    import org.apache.spark.sql.types.StringType
    import org.apache.spark.sql.types.IntegerType
    import org.apache.spark.sql.functions._
    
    object DailyUV {
      def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setAppName("DailyUV")
        val sc = new SparkContext(conf)
        val sqlContext = new SQLContext(sc)
        //构造用户 访问日志数据,并创建dataframe
        //要使用spark内置函数,就必须在这里代入sparkcontext下的隐式转换
        import sqlContext.implicits._
        //模拟用户访问日志,日志用逗号隔开,第一列是日期,第二列是用户id
        val userAccessLog = Array("2015-10-01,1122","2015-10-01,1122", "2015-10-01,1123", "2015-10-01,1125","2015-10-01,1125", "2015-10-02,1122", "2015-10-02,1124","2015-10-02,1122",  "2015-10-02,1123")
        val userAccessLogRDD= sc.parallelize(userAccessLog, 1);
        //将模拟出来的用户日志RDD,转换为dataframe
        //首先将普通的RDD转换为row的RDD
        val userAccessLogRowRDD = userAccessLogRDD.map(log =>Row(log.split(",")(0),log.split(",")(1).toInt))
      
        
        //构造dataframe元数据
        val structType = StructType(Array(StructField("date",StringType,true),StructField("userid",IntegerType,true)))
        val userAccessLogRowDF=sqlContext.createDataFrame(userAccessLogRowRDD, structType)
        
        //这里正式使用spark1.5.x版本的提供的最新特性,内置函数,countDistinct
        //每天都有很多用户来访问,但是每个用户可能每天都会访问很多次
        //UV:指的是对用户进行去重以后的访问总数
        //聚合函数的用法;
        //首先对dataframe调用groupBy()方法,对某一列进行分组
        //然后调用agg()方法,第一个参数,必须必须传入之前在groupBy方法中出现的字段
        //第二个参数,传入countDistinct、sum,first等,spark提供的内置函数
        //内置函数中,传入的参数也是用单引号作为前缀的,其他的字段
        
        
        userAccessLogRowDF.groupBy("date").agg('date, countDistinct('userid)).map(row => Row(row(1),row(2))).collect.foreach(println)
            
      }
    }
    
    
    统计每日的销售额
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.types.StructType
    import org.apache.spark.sql.types.StructField
    import org.apache.spark.sql.types.StringType
    import org.apache.spark.sql.types.DoubleType
    import org.apache.spark.sql.functions._
    
    object DailySale {
      
      val conf = new SparkConf().setMaster("local").setAppName("DailySale")
      val sc = new SparkContext(conf)
      val sqlContext = new SQLContext(sc)
      import sqlContext.implicits._
      
      //模拟元数据
      //实际上,有些时候,会出现日志的上报错误,比如日志里丢了用户的信息,那么这种,就一律不统计了
      val userSaleLog = Array("2015-10-01,100,1122","2015-10-01,100,1133","2015-10-01,100,","2015-10-02,300,1122","2015-10-02,200,1122","2015-10-02,100,","2015-10-02,100,1122")
    
      val userSaleLogRDD = sc.parallelize(userSaleLog, 1);
      
      //进行过滤
      
      val filteredUserSaleRowRDD = userSaleLogRDD.filter(log => if(log.split(",").length ==3) true else false)
      
      
      val userSaleLogRowRDD = filteredUserSaleRowRDD.map(log => Row(log.split(",")(0),log.split(",")(1).toDouble))
      
      val structType = StructType(Array(StructField("date",StringType,true),StructField("sale_amount",DoubleType,true)))
      
      val userSaleLogDF = sqlContext.createDataFrame(userSaleLogRowRDD, structType)
      //开始统计每日销售额的统计
      userSaleLogDF.groupBy("date").agg('date, sum('sale_amount)).map(row => Row(row(1),row(2))).collect().foreach(println)
      
      
    }
    七、开窗函数
    Spark1.4.x版本以后,为spark sql和dataframe引入了开窗函数,比如最经典最常用的row_number(),可以让我们实现分组取topN的逻辑
    
    案例:统计每个种类的销售额排名前3 的产品
    
    package com.spark.spark_sql;
    
    import org.apache.spark.SparkConf;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.hive.HiveContext;
    
    /**
     * 开窗函数row_number
     * @author Administrator
     */
    public class RowNumberWindowFunction {
    public static void main(String[] args) {
        
        SparkConf conf = new SparkConf().setAppName("NewNumberWindowFunction");
        JavaSparkContext sc = new JavaSparkContext(conf);
        HiveContext hiveContext = new HiveContext(sc.sc());
        
        //创建销售表sales表
        hiveContext.sql("drop table if exists sales");
        hiveContext.sql("create table if not exists sales (product string,category string ,revenue bigint) row format delimited fields terminated by '	'");
        hiveContext.sql("load data local inpath '/home/hadoop/sales.txt' into table sales");
        //开始编写我们的统计逻辑,使用row_number()开窗函数
        //先说明一下,row_number()开窗函数的作用
        //其实就是给每一个分组的数据,按照其排序顺序,打上一个分组内的行号
        //比如说,有一个分组date=20151001,里面有三条数据,1122 1121 1124 
        //那么对这个分组的每一行使用row_number()开窗函数以后,三行依次会获得一个组内的行号
        //行号从1开始递增,比如 1122 11121 21124 3
        
        DataFrame top3SalesDF = hiveContext.sql("select product,category,revenue from "
                + "(select product,category,revenue ,row_number() over (partition by category order by revenue desc ) rank from sales) tmp_sales  where rank <=3");
        
        //row_number()开窗函数的语法说明
        //首先,可以在select查询时,使用row_number()函数
        //其次,row_number()函数后面先跟上over关键字
        //然后括号中是partition by 也就是根据那个字段进行分组
        //其次是可以使用order by进行组内排序
        //然后row_number()就可以给每一个组内的行,一个组一个行号
        
        //将每组排名前三的数据,保存到一个表中
        hiveContext.sql("drop table if exists top3_sales");
        top3SalesDF.saveAsTable("top3_sales");
        sc.close();
    }
    }
    
    函数名(列) OVER(选项) 
    八、UDF自定义函数
    UDF:User Defined Function.用户自定义函数
    package com.spark.spark_sql
    
    import org.apache.spark.SparkConf
    import org.apache.spark.SparkContext
    import org.apache.spark.sql.SQLContext
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.types.StructType
    import org.apache.spark.sql.types.StructField
    import org.apache.spark.sql.types.StringType
    
    object UDF {
      def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setMaster("local").setAppName("UDF")
        val sc = new SparkContext(conf)
        val sqlContext = new SQLContext(sc)
        
        //构造模拟数据
        val names =Array("Leo","Marry","Jack","Tom")
        val namesRDD = sc.parallelize(names, 1)
        val namesRowRDD = namesRDD.map(name => Row(name))
        val structType =StructType(Array(StructField("name",StringType)))
        val namesRowDF =sqlContext.createDataFrame(namesRowRDD, structType)
        
        //注册一张name表
        namesRowDF.registerTempTable("names")
        
        //定义和注册自定义函数
        //定义函数
        //注册函数
        sqlContext.udf.register("strLen",(str:String) => str.length)
        
        //使用自己的函数
        sqlContext.sql("select name,strLen(name) from names").collect.foreach(println)
    
      }
    }
    九、UDAF自定义聚合函数
    UDAF:User Defined Aggregate Function。用户自定义聚合函数,是spark1.5.x引入的最新特性。
    UDF针对的是单行输入,返回一个输出,
    UDAF,则可以针对多行输入,进行聚合计算,返回一个输出,功能更加的强大
    
    Scala代码:
    import org.apache.spark.sql.Row
    import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
    import org.apache.spark.sql.types._
    
    
    class StringCount extends UserDefinedAggregateFunction {
      //inputschema   指的是,输入数据的数据
      override def inputSchema: StructType = {
        StructType(Array(StructField("str",StringType,true)))
      }
      //bufferSchema,指的是,中间进行聚合时,所处理的数据类型
      override def bufferSchema: StructType = {
        StructType(Array(StructField("count",IntegerType,true)))
      }
    
      //dataType,指的是,函数的返回值的类型
      override def dataType: DataType = {
       IntegerType
      }
      override def deterministic: Boolean = {
        true
      }
      //为每一个分组的数据执行初始化操作
      override def initialize(buffer: MutableAggregationBuffer): Unit = {
        buffer(0)=0
      }
      //指的是,每个分组,有新的值进来的时候,如何进行分组,对应的聚合值的计算
      override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
        buffer(0)=buffer.getAs[Int](0)+1
    
      }
      //由于spark是分布式的,所以一个分组的数据,可能会在不同的节点上进行局部的聚合,就是update
      //但是,最后一个分组,在各个节点上的聚合值,要执行merge,也就是合并
      override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
        buffer1(0)=buffer1.getAs[Int](0)+buffer2.getAs[Int](0)
      }
      //最后,指的是,一个分组的聚合,如何通过中间的缓存聚合值,随后返回一个最终的聚合值
      override def evaluate(buffer: Row): Any = {
        buffer.getAs[Int](0)
      }
    }
    
    
    十、工作原理以及性能调优
    
    1.工作原理
    Sqlparse、Analyser、Optimizer、SparkPlan
    
    
    
    2.性能调优
    1.设置shuffle过程中的并行度spark.sql.shuffle.partitions(sqlContext.setConf())
    2.在hive数据仓库建设过程中,合理设置数据类型,比如能设置为int的就不要设置为bigint.减少数据类型导致的不必要的内存开销
    3.编写sql时,尽量给出明确的列名,比如select name from students。不要写select * 的方式
    4.并行处理查询结果:对于spark sql查询的结果,如果数据量比较大的话,比如超多1000条,那么就不要一次collect到driver在处理。使用foreach()算子,并行处理查询结果
    5.缓存表:对于一条sql语句中可能多次使用到表,可以对其进行缓存没使用sqlContext.cache Table(tableName),或者DataFrame.cache()即可spark sql会用内存列存储的格式进行表的缓存。然后将spark sql就可以仅仅扫描需要使用的列,并且自动优化压缩,来最小化内存使用和GC开销。sqlcontext.uncacheTable(tableName)可以将表从缓存中移除。用sqlcontext.setConf(),设置spark.sql.inMemoryColumnarStorage.batchSize参数(默认10000),可以配置列存储单位
    
    6.广播join表:spark.sql.autoBroadcastJoinThreshold.默认是10485760(10MB),也就是在10M以内的表将其广播出去,在内存足够用的情况下,增加其大小,让更多的表广播出去,就可以将join中的较小的表广播出去,而不是进行网络数据传输了。
    7.钨丝计划:spark.sql.tungsten.enable,默认是true 自动管理内存
    
    最有效的就是第四点,缓存表和广播join表也是非常不错的
    
    
    
    十一、Hive on spark 
    背景:
    Hive是目前大数据领域,事实上的sql标准。其底层默认是基于MapReduce实现的。但是由于mapreduce速度是在比较慢。因此这两年,陆续出来了新的sql查询引擎。包括spark sql,hive on Taz ,hive on spark 
    
    Spark sql 与hive on spark 是不一样的,spark sql 自己研发出来的针对各种数据源,包括hive、json、parquet、JDBC、RDD等都可以执行查询的,一套基于spark计算引擎的查询引擎。因此它是spark的一个项目,只不过提供了针对hive执行查询的功能而已。适合在一些使用spark技术栈的大数据应用类系统中使用。
    
    而hive on spark是hive 的一个项目,他是指,不通过mapreudce作为唯一的引擎,而是将spark作为底层的查询引擎,hive on spark ,只适用于hive,在可预见的未来,很有可能hive默认的底层引擎从mapreduce切换为spark,适合于将原有的hive数据仓库以及数据分析替换为spark引擎,作为公司通用大数据统计计算分析引擎
    
    Hive基本的工作原理:
    hiveQL语句==>
    语法分==>AST==>
    生成逻辑执行计划==>operator Tree==>
    优化逻辑执行计划==>optimized operator Tree==>
    生成物理执行计划==>Task Tree==>
    优化物理执行计划==>Optimized Task Tree==>
    执行优化后的Optimized  Task Tree
    
    
    
    Hive on spark 的计算原理有如下的几个要点:
    1.将hive表作为spark RDD来进行操作
    
    2.使用hive 原语
    对于一些针对RDD的操作,比如,groupByKey(),sortByKey()等等。不使用spark的transformation操作和原语。如果那样做的话,那么就需要重新实现一套hive的原语,而且hive增加了新功能,那么又要实现新的spark 原语。因此,选择将hive的原语包装为针对RDD的操作即可
    
    3.新的物理执行计划生成机制
    使用sparkCompiler将逻辑执行计划,集=即Operator Tree,转换为Task tree。提交spark task给spark进行执行。Sparktask包装了DAG,DAG包装为sparkwork,Sparktask根据sparkwork表示的DAG计算
    
    4.sparkContext生命周期
    Hive on spark 会为每一个用户的会话,比如执行一次sql语句,创建一个sparkcontext,但是spark不允许在一个JVM内创建多个sparkcontext,因此,需要在单独额JVM中启动每一个会话的sparkcontext,然后通过RPC与远程JVM中的sparkContext进行通信。
    
    5.本地和远程运行模式
    Hive on spark 提供两种运行模式,本地和远程。如果将spark master设置为local
    
    
    
    
    
    
    十二、Spark sql与spark core整合
    案例:每日top3热点搜索词统计案例
    日志格式:
    日期、用户、搜索词、城市、平台、版本
    
    需求:
    1.筛选出符合条件(城市、平台、版本))的数据
    2.统计出每天搜索的UV排名前三的搜索词
    3.按照每天的top3搜索词的UV搜总次数,倒序排列
    4.将数据保存到hive表中
    
    实现思路分析
    1.针对原始的数据(HDFS文件),获取输入RDD
    2.使用filter算子,去针对输入RDD中的数据,进行数据过滤,过滤出符合条件的数据
    2.1普通的做法:直接在filter算子函数中,使用外部的查询条件(map),但是这样的话,是不是查询条件map,会发送到每一个task上一份副本(性能并不好)
    2.2优化后的做法:将查询条件,封装为Broadcast广播变量,在filter算子中使用Broadcast广播变量
    
    3.将数据转换为“日期_搜索词,用户”的格式,然后对他进行分组,再次进行映射。对每天每个搜索词的搜索用户进行去重操作,并统计去重后的数量,即为每天每个搜索词的UV,最后获得(日期_搜索词,UV)
    
    4.将得到的每天的搜索词的UV,RDD映射为元素类型的Row的RDD,将RDD转换为dataframe
    5.将dataframe注册为临时表,使用spark sql的开窗函数,来统计每天的UV排名前三的搜索词,以及他的搜索UV,最后获知,是一个dateframe
    6.将dateframe转换为RDD。继续操作,按照每天日期进行分组。并进行映射。计算出每天的top3所搜词的搜索uv的总数,然后将UV总数作为key,将每天的top3搜索词以及搜索次数,拼接为一个字符串
    7.按照每天的top3搜索总UV,进行排序,倒序排序
    8.将排好序的数据,再次映射回来,变成“日期_搜索词_UV”的格式
    9.再次映射为dataframe,并将数据保存到hive表中
    
    package com.spark.spark_sql;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    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.FlatMapFunction;
    import org.apache.spark.api.java.function.Function;
    import org.apache.spark.api.java.function.PairFunction;
    import org.apache.spark.broadcast.Broadcast;
    import org.apache.spark.sql.DataFrame;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.RowFactory;
    import org.apache.spark.sql.hive.HiveContext;
    import org.apache.spark.sql.types.DataTypes;
    import org.apache.spark.sql.types.DateType;
    import org.apache.spark.sql.types.StructField;
    import org.apache.spark.sql.types.StructType;
    
    import scala.Tuple2;
    
    /**
     * 每日top3热点搜索词统计
     * @author Administrator
     *
     */
    public class DailyTop3Keyword {
    
        public static void main(String[] args) {
            SparkConf conf = new SparkConf().setAppName("DailyTop3Keyword");
            JavaSparkContext jsc = new JavaSparkContext(conf);
            HiveContext hiveContext = new HiveContext(jsc.sc());
            //针对hdfs上的文件中的日志,获取输入的RDD
            JavaRDD<String> rawRDD = jsc.textFile("hdfs://hadoop01:8020/keyword.txt");
            //伪造一份数据,查询条件
            //备注:实际上,在实际的企业的项目开发中,很可能,这个查询条件是J2EE平台发送到MySQL表中的
            //然后,这里实际上通常是会用Spring框架和ORM框架的,去提取MySQL表中的查询条件
            Map<String,List<String>> queryParamMap =new HashMap<String, List<String>>();
            queryParamMap.put("city", Arrays.asList("beijing"));
            queryParamMap.put("platform", Arrays.asList("android"));
            queryParamMap.put("version", Arrays.asList("1.0","1.2","1.5","2.0"));
            
            //将map封装为广播变量,每个work节点只拷贝一份数据即可,这样可以进行优化
            final Broadcast<Map<String, List<String>>> queryParamMapBroadcast = jsc.broadcast(queryParamMap);
            
            //使用广播变量进行筛选
            JavaRDD<String> filterRDD = rawRDD.filter(new Function<String, Boolean>() {
                private static final long serialVersionUID = 1L;
    
                @Override
                public Boolean call(String log) throws Exception {
                    String[] logSplited = log.split("	");
                    String city =logSplited[3];
                    String platform =logSplited[4];
                    String version = logSplited[5];
                    //与查询条件进行比较,任何一个
                    Map<String, List<String>> queryParamMap = queryParamMapBroadcast.value();
                    
                    List<String> cities = queryParamMap.get("city");
                    if(cities.size()>0 && !cities.contains(city)){
                        return false;
                    }
                    
                    List<String> platforms = queryParamMap.get("city");
                    if(platforms.size()>0 && !platforms.contains(platform)){
                        return false;
                    }
                    List<String> versions = queryParamMap.get("city");
                    if(versions.size()>0 && !versions.contains(version)){
                        return false;
                    }
                    
                    return true;
                } 
            });
            
            //将过滤出来的原始的日志进行映射为(日期_搜索词,用户)的格式
            JavaPairRDD<String, String> dateKeywordUserRDD = filterRDD.mapToPair(new PairFunction<String, String, String>() {
    
                
                private static final long serialVersionUID = 1L;
    
                @Override
                public Tuple2<String, String> call(String t) throws Exception {
                     String[] logSplited = t.split("	");
                     String date =logSplited[0];
                     String user =logSplited[1];
                     String keyword =logSplited[2];
        
                    return new Tuple2<String, String>(date+"_"+keyword, user);
                }
            });
            
            //进行分组,获取每天的搜索词,有哪些用户进行搜索了(没有去重)
            JavaPairRDD<String, Iterable<String>> dateKeywordUsersRDD = dateKeywordUserRDD.groupByKey();
            
            //对每天每个搜索词的搜索用户,执行去重操作,获得其UV值
            JavaPairRDD<String, Long> dateKeywordUVRDD = dateKeywordUsersRDD.mapToPair(new PairFunction<Tuple2<String,Iterable<String>>, String, Long>() {
    
                private static final long serialVersionUID = 1L;
    
                @Override
                public Tuple2<String, Long> call(Tuple2<String, Iterable<String>> t) throws Exception {
                    String  dateKeyword = t._1;
                    Iterator<String> users = t._2.iterator();
                    //对用户进行去重,并且统计数量
                    List<String> distinctUsers = new ArrayList<String>();
                    
                    while(users.hasNext()){
                        String user = users.next();
                        if(!distinctUsers.contains(user)){
                            distinctUsers.add(user);
                        }
                    }
                    //获取uv
                    long uv = distinctUsers.size();
                    return new Tuple2<String, Long>(dateKeyword, uv);
                }
            });
            
            //将每天每个搜索词的UV数据转换成dataframe
            JavaRDD<Row> dateKeywordUVRowRDD = dateKeywordUVRDD.map(new Function<Tuple2<String,Long>, Row>() {
    
                @Override
                public Row call(Tuple2<String, Long> v1) throws Exception {
                    String date = v1._1.split("_")[0];
                    String keyword = v1._1.split("_")[1];
                    long uv = v1._2;
                    return RowFactory.create(date,keyword,uv);
                }
            });
            
            List<StructField> structFields = Arrays.asList(
                    DataTypes.createStructField("date", DataTypes.StringType, true),
                    DataTypes.createStructField("keyword", DataTypes.StringType,true),
                    DataTypes.createStructField("uv", DataTypes.LongType,true)
                    );
            
            StructType structType =DataTypes.createStructType(structFields);
            DataFrame dateKeywordUVDF = hiveContext.createDataFrame(dateKeywordUVRowRDD, structType);
            
            //使用spark sql的开窗函数,统计每天的搜索排名前三的热点搜索词
            dateKeywordUVDF.registerTempTable("daily_keyword_uv");
            
            
            DataFrame dailyTop3KeywordDF = hiveContext.sql("select date,keyword,uv from "
                    + "(select date,keyword,uv,row_number over (partition by date order by uv desc ) rank from daily_keyword_uv )tmp "
                    + "where rank <=3");
            
            //将dateframe 转换为RDD,然后映射,计算出每天的top3搜索词的搜索uv的总数
            JavaPairRDD<String, String> Top3DateKeywordUvRDD = dailyTop3KeywordDF.javaRDD().mapToPair(new PairFunction<Row, String, String>() {
    
                @Override
                public Tuple2<String, String> call(Row row) throws Exception {
                    String date = String.valueOf(row.get(0));
                    String keyword =String.valueOf(row.get(1));
                    Long uv = Long.valueOf(String.valueOf(row.get(2)));
                    
                    return new Tuple2<String, String>(date, keyword+"_"+uv);
                }
            });
            
            JavaPairRDD<String, Iterable<String>> Top3DateKeywordRDD = Top3DateKeywordUvRDD.groupByKey();
            JavaPairRDD<Long, String> uvDateKeywordsRDD = Top3DateKeywordRDD.mapToPair(new PairFunction<Tuple2<String,Iterable<String>>, Long, String>() {
    
                @Override
                public Tuple2<Long, String> call(Tuple2<String, Iterable<String>> t) throws Exception {
                    String date = t._1;
                    
                    Iterator<String> keywordUvIterator = t._2.iterator();
                    Long totalUv = 0L;
                    String dateKeywordUv = date;
                    while(keywordUvIterator.hasNext()){
                        String keywordUv= keywordUvIterator.next();
                        Long uv = Long.valueOf(keywordUv.split("_")[1]);
                        totalUv+=uv;
                        dateKeywordUv+=","+keywordUv;
                    }
                    return new Tuple2<Long, String>(totalUv, dateKeywordUv);
                }
            });
            //按照每天的总搜索进行倒序排序
            
            JavaPairRDD<Long, String> sortedUvDateKeywordsRDD = uvDateKeywordsRDD.sortByKey(false);
            
            //在此进行映射,将排序后的数据,映射回原始的格式Iterable<ROW>
            JavaRDD<Row> sortedRowRDD = sortedUvDateKeywordsRDD.flatMap(new FlatMapFunction<Tuple2<Long,String>, Row>() {
    
                @Override
                public Iterable<Row> call(Tuple2<Long, String> t) throws Exception {
                    String dateKeywords=t._2;
                    String[] dateKeywordsSplited = dateKeywords.split(",");
                    String date = dateKeywordsSplited[0];
                    List<Row> rows= new ArrayList<Row>();
                    rows.add(RowFactory.create(date,
                            dateKeywordsSplited[1].split("_")[0],
                            Long.valueOf(dateKeywordsSplited[1].split("_")[1])
                            ));
                    rows.add(RowFactory.create(date,
                            dateKeywordsSplited[1].split("_")[0],
                            Long.valueOf(dateKeywordsSplited[2].split("_")[1])
                            ));
                    rows.add(RowFactory.create(date,
                            dateKeywordsSplited[1].split("_")[0],
                            Long.valueOf(dateKeywordsSplited[3].split("_")[1])
                            ));    
                    return rows;
                }
            });
            
            //将最终的数据转换为dateframe
            DataFrame finalDF = hiveContext.createDataFrame(sortedRowRDD, structType);
            
            finalDF.saveAsTable("daily_top3_keyword_uv");
            jsc.close();
        }
    }
  • 相关阅读:
    E. Gosha is hunting (dp + wqs套wqs)
    【Codeforces Round #575 (Div. 3) 】 RGB Substring (hard version) ( FFT)
    C
    poj 1160 Post Office(dp + wqs二分)
    【 2018南京 】Magic Potion (网络流)
    【 2018南京 】Kangaroo Puzzle (思维+暴力模拟)
    【 2018南京 】Country Meow (模拟退火)
    【2018焦作网络赛】 Jiu Yuan Wants to Eat (熟练剖分 + 思维)
    【2018焦作网络赛】 Modular Production Line(费用流)
    【2018焦作网络赛】 B. Mathematical Curse (dp)
  • 原文地址:https://www.cnblogs.com/yin-fei/p/10780003.html
Copyright © 2020-2023  润新知