Spark SQL

  1. 简介

    • SparkSQL 的前身是 Shark, SparkSQL 产生的根本原因是其完全脱离了 Hive 的限制。(Shark 底层依赖于 Hive 的解析器, 查询优化器)

      • SparkSQL 支持查询原生的 RDD。
      • 能够在 scala/java 中写 SQL 语句。 支持简单的 SQL 语法检查, 能够在 Scala 中 写Hive 语句访问 Hive 数据, 并将结果取回作为RDD使用
  2. Spark on Hive 和 Hive on Spark

    • Spark on Hive: Hive 只作为储存角色, Spark负责 sql 解析优化, 执行。
    • Hive on Spark: Hive 即作为存储又负责 sql 的解析优化, Spark 负责执行。
  3. Dataset 与 DataFrame

    • Dataset 是一个分布式数据容器,与 RDD 类似, 然而 DataSet 更像 传统数据库的二维表格, 除了数据以外, 还掌握的结构信息, 即schema。
    • 同时, 与 Hive 类似, Dataset 也支持嵌套数据类型 (struct、array 和 map)。
    • 从 API 易用性角度上看, DataSet API 提供的是一套高层的关系操作, 比函数式的 RDD API 更加友好, 门槛更低。
    • Dataset 的底层封装的是RDD, 当 RDD 的泛型是 Row 类型的时候, 我们可以可以称它为 DataFrame。即 Dataset = DataFrame
  4. SparkSQL 的数据源

    • SparkSQL的数据源可以是JSON类型的字符串, JDBC, Parquent, Hive, HDFS 等。

  5. SparkSQL 底层架构

    • 首先拿到 sql 后解析一批未被解决的逻辑计划, 再经过分析得到分析后的逻辑计划, 再经过一批优化规则转换成一批最佳优化的逻辑计划, 再经过一批优化规则转换成一批最佳优化的逻辑计划, 再经过 SparkPlanner 测策略转化成一批物理计划, 随后经过消费模型转换成一个个的Spark任务执行。

  6. 谓词下推 (predicate Pushdown)

    • 从关系型数据库借鉴而来, 关系型数据中谓词下推到外部数据库用以减少数据传输
    • 基本思想: 尽可能早的处理表达式
    • 属于逻辑优化, 优化器将谓词过滤下推到数据源, 使物理执行跳过无关数据
    • 参数打开设置: hive.optimize.ppd=true

创建 Dataset 的几种方式

  1. 读取 json 格式的文件创建 DataSet

    • 注意事项:

      • json 文件中的 json 数据不能嵌套 json 格式数据。

      • Dataset 是一个一个 Row 类型的 RDD, ds.rdd()/ds.javaRDD()。

      • 可以两种方式读取json格式的文件。

      • df.show() 默认显示前 20 行数据。

      • Dataset 原生 API 可以操作 Dataset(不方便)。

      • 注册成临时表时, 表中的列默认按 ascii 顺序显示列。

      • 案例:

        • json:

          {"name":"burning","age": 18}
          {"name":"atme"}
          {"name":"longdd","age":18}
          {"name":"yyf","age":28}
          {"name":"zhou","age":20}
          {"name":"blaze"}
          {"name":"ocean","age":18}
          {"name":"xiaoliu","age":28}
          {"name":"zhangsan","age":28}
          {"name":"lisi"}
          {"name":"wangwu","age":18}
        • Java代码:

        package com.ronnie.java.json;
        
        import org.apache.spark.api.java.JavaRDD;
        import org.apache.spark.sql.Dataset;
        import org.apache.spark.sql.Row;
        import org.apache.spark.sql.SparkSession; public class ReadJson { public static void main(String[] args) {
        SparkSession sparkSession = SparkSession.builder().appName("jsonFile").master("local").getOrCreate(); /**
        * Dataset的底层是一个个的 RDD, RDD的泛型是Row类型。
        * 以下两种方式都可以读取json格式的文件
        */ Dataset<Row> ds = sparkSession.read().format("json").load("./resources/json");
        // Dataset<Row> ds = sparkSession.read().json("data/json");
        ds.show();
        /**
        * +----+--------+
        * | age| name|
        * +----+--------+
        * | 18| burning|
        * |null| atme|
        * | 18| longdd|
        * | 28| yyf|
        * | 20| zhou|
        * |null| blaze|
        * | 18| ocean|
        * | 28| xiaoliu|
        * | 28|zhangsan|
        * |null| lisi|
        * | 18| wangwu|
        * +----+--------+
        */ /**
        * Dataset 转换为 RDD
        */ JavaRDD<Row> javaRDD = ds.javaRDD(); /**
        * 显示 Dataset 中的内容, 默认显示前20行. 如果显示多行要指定多少行show(行数)
        * 注意: 当有多个列时, 显示的列先后顺序是按列的ascii码顺序先后显示
        */ /**
        * 树形的形式显示schema信息
        */
        ds.printSchema(); /**
        * root
        * |-- age: long (nullable = true)
        * |-- name: string (nullable = true)
        */ /**
        * Dataset自带的API 操作Dataset
        */ // select name from table
        ds.select("name").show();
        /**
        * +--------+
        * | name|
        * +--------+
        * | burning|
        * | atme|
        * | longdd|
        * | yyf|
        * | zhou|
        * | blaze|
        * | ocean|
        * | xiaoliu|
        * |zhangsan|
        * | lisi|
        * | wangwu|
        * +--------+
        */ // select name age+10 as addage from table
        ds.select(ds.col("name"),ds.col("age").plus(10).alias("addage")).show();
        /**
        * +--------+------+
        * | name|addage|
        * +--------+------+
        * | burning| 28|
        * | atme| null|
        * | longdd| 28|
        * | yyf| 38|
        * | zhou| 30|
        * | blaze| null|
        * | ocean| 28|
        * | xiaoliu| 38|
        * |zhangsan| 38|
        * | lisi| null|
        * | wangwu| 28|
        * +--------+------+
        */ // select name, age from table where age > 19
        ds.select(ds.col("name"),ds.col("age")).where(ds.col("age").gt(19)).show(); /**
        * +--------+---+
        * | name|age|
        * +--------+---+
        * | yyf| 28|
        * | zhou| 20|
        * | xiaoliu| 28|
        * |zhangsan| 28|
        * +--------+---+
        */ // select count(*) from table group by age
        ds.groupBy(ds.col("age")).count().show();
        /**
        * +----+-----+
        * | age|count|
        * +----+-----+
        * |null| 3|
        * | 28| 3|
        * | 18| 4|
        * | 20| 1|
        * +----+-----+
        */ /**
        * 将Dataset 注册成临时的一张表, 这张表临时注册到内存中, 是逻辑上的表, 不会雾化到磁盘
        */ ds.createOrReplaceTempView("jtable"); Dataset<Row> result = sparkSession.sql("select age, count(*) as gege from jtable group by age"); result.show(); /**
        * +----+----+
        * | age|gege|
        * +----+----+
        * |null| 3|
        * | 28| 3|
        * | 18| 4|
        * | 20| 1|
        * +----+----+
        */
        sparkSession.stop();
        }
        }
  2. 通过 json 格式的 RDD 创建 DataSet

    package com.ronnie.java.json;
    
    import org.apache.spark.SparkContext;
    import org.apache.spark.api.java.JavaRDD;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.Dataset;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.SparkSession; import java.util.Arrays; public class CreateDatasetFromJsonRDD { public static void main(String[] args) {
    SparkSession sparkSession = SparkSession.builder().appName("jsonrdd").master("local").getOrCreate();
    SparkContext sc = sparkSession.sparkContext();
    JavaSparkContext jsc = new JavaSparkContext(sc); JavaRDD<String> nameRDD = jsc.parallelize(Arrays.asList(
    "{'name':'hao','age':\"24\"}",
    "{\"name\":\"mu\",\"age\":\"26\"}",
    "{\"name\":\"xiao8\",\"age\":\"27\"}"
    )); JavaRDD<String> scoreRDD = jsc.parallelize(Arrays.asList(
    "{\"name\":\"zhangsan\",\"score\":\"100\"}",
    "{\"name\":\"mu\",\"score\":\"200\"}",
    "{\"name\":\"wangwu\",\"score\":\"300\"}"
    )); Dataset<Row> nameds = sparkSession.read().json(nameRDD);
    Dataset<Row> scoreds = sparkSession.read().json(scoreRDD);
    nameds.createOrReplaceTempView("nameTable");
    scoreds.createOrReplaceTempView("scoreTable"); Dataset<Row> result = sparkSession.sql("select nameTable.name, nameTable.age, scoreTable.score "
    + "from nameTable join scoreTable "
    + "on nameTable.name = scoreTable.name");
    result.show();
    /**
    * +----+---+-----+
    * |name|age|score|
    * +----+---+-----+
    * | mu| 26| 200|
    * +----+---+-----+
    */
    sparkSession.stop();
    }
    }
  3. 非 json 格式的 RDD 创建 DataSet

    • 通过反射的方式将非 json 格式的RDD转换成 Dataset

      • 自定义类要可序列化
      • 自定义类的访问级别是 Public
      • RDD 转成 Dataset 后会根据映射将字段 ASCII 码排序
      • 将 Dataset 转换成 RDD时获取字段的两种方式:
        • ds.getInt(0) 下标获取(不推荐使用)
        • ds.getAs("列名") 获取(推荐使用)
      • person.txt

      1,longdd,27
      2,yyf,26
      3,zhou,27
      4,burning,30
      5,atme,21
      • Person.java
      package com.ronnie.java.entity;
      import java.io.Serializable; public class Person implements Serializable {
      private static final long serialVersionUID = 1L; private String id ;
      private String name;
      private Integer age; public String getId() {
      return id;
      } public void setId(String id) {
      this.id = id;
      } public String getName() {
      return name;
      } public void setName(String name) {
      this.name = name;
      } public Integer getAge() {
      return age;
      } public void setAge(Integer age) {
      this.age = age;
      } @Override
      public String toString() {
      return "Person{" +
      "id='" + id + '\'' +
      ", name='" + name + '\'' +
      ", age=" + age +
      '}';
      }
      }
      • CreateDatasetRDDWithReflect

        package com.ronnie.java.json;
        
        import com.ronnie.java.entity.Person;
        import org.apache.spark.SparkContext;
        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.Dataset;
        import org.apache.spark.sql.Row;
        import org.apache.spark.sql.SparkSession; public class CreateDatasetFromRDDWithReflect { public static void main(String[] args) {
        SparkSession sparkSession = SparkSession.builder().appName("reflect").master("local").getOrCreate(); SparkContext sc = sparkSession.sparkContext(); JavaSparkContext jsc = new JavaSparkContext(sc); JavaRDD<String> lineRDD = jsc.textFile("./resources/person.txt"); JavaRDD<Person> personRDD = lineRDD.map(new Function<String, Person>() {
        private static final long serialVersionUID = 1L; @Override
        public Person call(String line) throws Exception {
        Person p = new Person();
        p.setId(line.split(",")[0]);
        p.setName(line.split(",")[1]);
        p.setAge(Integer.valueOf(line.split(",")[2])); return p;
        }
        });
        /**
        * 传入进去Person.class的时候,sqlContext是通过反射的方式创建DataFrame
        * 在底层通过反射的方式获得Person的所有field,结合RDD本身,就生成了DataFrame
        */
        Dataset<Row> dataFrame = sparkSession.createDataFrame(personRDD, Person.class);
        dataFrame.show();
        /**
        * +---+---+-------+
        * |age| id| name|
        * +---+---+-------+
        * | 27| 1| longdd|
        * | 26| 2| yyf|
        * | 27| 3| zhou|
        * | 30| 4|burning|
        * | 21| 5| atme|
        * +---+---+-------+
        */
        dataFrame.printSchema();
        /**
        * root
        * |-- age: integer (nullable = true)
        * |-- id: string (nullable = true)
        * |-- name: string (nullable = true)
        */
        dataFrame.registerTempTable("person");
        Dataset<Row> sql = sparkSession.sql("select name, id, age from person where id = 2");
        sql.show();
        /**
        * +----+---+---+
        * |name| id|age|
        * +----+---+---+
        * | yyf| 2| 26|
        * +----+---+---+
        */ /**
        * 将Dataset转成JavaRDD
        * 注意:
        * 1.可以使用row.getInt(0),row.getString(1)...通过下标获取返回Row类型的数据,但是要注意列顺序问题---不常用
        * 2.可以使用row.getAs("列名")来获取对应的列值。
        */
        JavaRDD<Row> javaRDD = dataFrame.javaRDD(); JavaRDD<Person> map = javaRDD.map(new Function<Row, Person>() { private static final long serialVersionUID = 1L; @Override
        public Person call(Row row) throws Exception {
        Person p = new Person(); p.setId(row.getAs("id"));
        p.setName(row.getAs("name"));
        p.setAge(row.getAs("age")); return p;
        }
        });
        map.foreach(x -> System.out.println(x));
        /**
        * Person{id='1', name='longdd', age=27}
        * Person{id='2', name='yyf', age=26}
        * Person{id='3', name='zhou', age=27}
        * Person{id='4', name='burning', age=30}
        * Person{id='5', name='atme', age=21}
        */ sc.stop();
        }
        }
    • 动态创建 Schema 将非 json 格式的 RDD 转换成 Dataset

      package com.ronnie.java.json;
      
      import org.apache.spark.SparkContext;
      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.Dataset;
      import org.apache.spark.sql.Row;
      import org.apache.spark.sql.RowFactory;
      import org.apache.spark.sql.SparkSession;
      import org.apache.spark.sql.types.DataTypes;
      import org.apache.spark.sql.types.StructField;
      import org.apache.spark.sql.types.StructType; import java.util.Arrays;
      import java.util.List; public class CreateDatasetFromRDDWithStruct {
      public static void main(String[] args) {
      SparkSession sparkSession = SparkSession.builder().appName("schema").master("local").getOrCreate(); SparkContext sc = sparkSession.sparkContext(); JavaSparkContext jsc = new JavaSparkContext(sc); JavaRDD<String> lineRDD = jsc.textFile("./resources/person.txt"); /**
      * 转换成Row类型的RDD
      */ final JavaRDD<Row> rowRDD = lineRDD.map(new Function<String, Row>() { private static final long serialVersionUID = 1L; @Override
      public Row call(String line) throws Exception {
      return RowFactory.create(
      line.split(",")[0],
      line.split(",")[1],
      Integer.valueOf(line.split(",")[2])
      );
      }
      }); /**
      * 动态构建DataFrame中的元数据,一般来说这里的字段可以来源自字符串,也可以来源于外部数据库
      */ List<StructField> asList = Arrays.asList(
      DataTypes.createStructField("id", DataTypes.StringType, true),
      DataTypes.createStructField("name", DataTypes.StringType, true),
      DataTypes.createStructField("age", DataTypes.IntegerType, true)
      ); StructType schema = DataTypes.createStructType(asList); Dataset<Row> df = sparkSession.createDataFrame(rowRDD, schema); df.printSchema();
      /**
      * root
      * |-- id: string (nullable = true)
      * |-- name: string (nullable = true)
      * |-- age: integer (nullable = true)
      */
      df.show();
      /**
      * +---+-------+---+
      * | id| name|age|
      * +---+-------+---+
      * | 1| longdd| 27|
      * | 2| yyf| 26|
      * | 3| zhou| 27|
      * | 4|burning| 30|
      * | 5| atme| 21|
      * +---+-------+---+
      */ sc.stop(); }
      }
  4. 读取 parquet 文件创建 DataSet

    • SaveMode: 指文件保存时的模式
    • Overwrite: 覆盖
    • Append: 追加
    • getOrCreate: 获取或创建
    package com.ronnie.java.parquet;
    
    import org.apache.spark.sql.Dataset;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.SaveMode;
    import org.apache.spark.sql.SparkSession; public class CreateDataFrameFromParquet {
    public static void main(String[] args) {
    SparkSession sparkSession = SparkSession.builder().appName("parquet").master("local").getOrCreate(); Dataset<Row> df = sparkSession.read().json("./resources/json"); df.show();
    /**
    * +----+--------+
    * | age| name|
    * +----+--------+
    * | 18| burning|
    * |null| atme|
    * | 18| longdd|
    * | 28| yyf|
    * | 20| zhou|
    * |null| blaze|
    * | 18| ocean|
    * | 28| xiaoliu|
    * | 28|zhangsan|
    * |null| lisi|
    * | 18| wangwu|
    * +----+--------+
    */ /**
    * 将Dataset保存成parquet文件,
    * SaveMode指定存储文件时的保存模式:
    * Overwrite:覆盖
    * Append:追加
    * ErrorIfExists:如果存在就报错
    * Ignore:如果存在就忽略
    * 保存成parquet文件有以下两种方式:
    */ // df.write().mode(SaveMode.Overwrite).format("parquet").save("./resources/parquet");
    df.write().mode(SaveMode.Overwrite).parquet("./resources/parquet");
    /**
    * {
    * "type" : "struct",
    * "fields" : [ {
    * "name" : "age",
    * "type" : "long",
    * "nullable" : true,
    * "metadata" : { }
    * }, {
    * "name" : "name",
    * "type" : "string",
    * "nullable" : true,
    * "metadata" : { }
    * } ]
    * }
    * and corresponding Parquet message type:
    * message spark_schema {
    * optional int64 age;
    * optional binary name (UTF8);
    * }
    */ /**
    * 加载parquet文件成Dataset
    * 加载parquet文件有以下两种方式:
    */ Dataset<Row> load = sparkSession.read().format("parquet").load("./resources/parquet"); load.show();
    /**
    * +----+--------+
    * | age| name|
    * +----+--------+
    * | 18| burning|
    * |null| atme|
    * | 18| longdd|
    * | 28| yyf|
    * | 20| zhou|
    * |null| blaze|
    * | 18| ocean|
    * | 28| xiaoliu|
    * | 28|zhangsan|
    * |null| lisi|
    * | 18| wangwu|
    * +----+--------+
    */ sparkSession.stop();
    }
    }
  5. 读取 JDBC 中的数据创建 DataSet

    package com.ronnie.java.jdbc;
    
    import org.apache.spark.sql.*;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties; public class CreateDatasetFromMysql { public static void main(String[] args) {
    SparkSession sparkSession = SparkSession.builder().appName("jdbc").master("local").getOrCreate(); /**
    * 第一种方式读取MySql数据库表,加载为Dataset
    */ Map<String, String> options = new HashMap<>(); options.put("url", "jdbc:mysql://localhost:3306/spark");
    options.put("driver", "com.mysql.jdbc.Driver");
    options.put("user", "root");
    options.put("password", "123456");
    options.put("dbtable", "person"); Dataset<Row> person = sparkSession.read().format("jdbc").options(options).load(); person.show();
    /**
    * +---+------+---+
    * | id| name|age|
    * +---+------+---+
    * | 1| slark| 70|
    * | 2| pom| 40|
    * | 3|huskar| 60|
    * | 4| axe| 80|
    * +---+------+---+
    */ person.createOrReplaceTempView("person"); /**
    * 第二种方式读取MySql数据表加载为Dataset
    */
    DataFrameReader reader = sparkSession.read().format("jdbc");
    reader.option("url", "jdbc:mysql://localhost:3306/spark");
    reader.option("driver", "com.mysql.jdbc.Driver");
    reader.option("user", "root");
    reader.option("password", "123456");
    reader.option("dbtable", "score"); Dataset<Row> score = reader.load();
    score.show();
    /**
    * +---+------+-----+
    * | id| name|score|
    * +---+------+-----+
    * | 1|dragon| 80|
    * | 2| axe| 99|
    * | 3| slark| 81|
    * +---+------+-----+
    */ score.createOrReplaceTempView("score");
    Dataset<Row> result =
    sparkSession.sql("select person.id,person.name,person.age,score.score "
    + "from person,score "
    + "where person.name = score.name and score.score> 82");
    result.show();
    /**
    * +---+----+---+-----+
    * | id|name|age|score|
    * +---+----+---+-----+
    * | 4| axe| 80| 99|
    * +---+----+---+-----+
    */ result.registerTempTable("result"); /**
    * 将Dataset结果保存到Mysql中
    */
    //
    Properties properties = new Properties();
    properties.setProperty("user", "root");
    properties.setProperty("password", "123456"); /**
    * SaveMode:
    * Overwrite:覆盖
    * Append:追加
    * ErrorIfExists:如果存在就报错
    * Ignore:如果存在就忽略
    *
    */ result.write().mode(SaveMode.Overwrite).jdbc("jdbc:mysql://127.0.0.1:3306/spark", "result", properties);
    // System.out.println("----Finish----");
    sparkSession.stop();
    }
    }
  6. Hive 中的数据创建 DataSet

    package com.ronnie.java.hive;
    
    import org.apache.spark.sql.Dataset;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.SaveMode;
    import org.apache.spark.sql.SparkSession; public class CreateDatasetFromHive { public static void main(String[] args) {
    SparkSession sparkSession = SparkSession
    .builder()
    .appName("hive")
    //开启hive的支持,接下来就可以操作hive表了
    // 前提需要是需要开启hive metastore 服务
    .enableHiveSupport()
    .getOrCreate(); sparkSession.sql("USE spark");
    sparkSession.sql("DROP TABLE IF EXISTS student_infos");
    //在hive中创建student_infos表
    sparkSession.sql("CREATE TABLE IF NOT EXISTS student_infos (name STRING,age INT) row format delimited fields terminated by '\t' ");
    sparkSession.sql("load data local inpath '/root/student_infos' into table student_infos");
    //注意:此种方式,程序需要能读取到数据(如/root/student_infos),同时也要能读取到 metastore服务的配置信息。 sparkSession.sql("DROP TABLE IF EXISTS student_scores");
    sparkSession.sql("CREATE TABLE IF NOT EXISTS student_scores (name STRING, score INT) row format delimited fields terminated by '\t'");
    sparkSession.sql("LOAD DATA "
    + "LOCAL INPATH '/root/student_scores'"
    + "INTO TABLE student_scores"); // Dataset<Row> df = hiveContext.table("student_infos");//读取Hive表加载Dataset方式
    /**
    * 查询表生成Dataset
    */
    Dataset<Row> goodStudentsDF = sparkSession.sql("SELECT si.name, si.age, ss.score "
    + "FROM student_infos si "
    + "JOIN student_scores ss "
    + "ON si.name=ss.name "
    + "WHERE ss.score>=80"); goodStudentsDF.registerTempTable("goodstudent");
    Dataset<Row> result = sparkSession.sql("select * from goodstudent");
    result.show(); /**
    * 将结果保存到hive表 good_student_infos
    */
    sparkSession.sql("DROP TABLE IF EXISTS good_student_infos");
    goodStudentsDF.write().mode(SaveMode.Overwrite).saveAsTable("good_student_infos"); sparkSession.stop();
    } }

序列化问题

  • Java 中以下几种情况下不被序列化的问题:

    • 反序列化时 serializable 版本号不一致导致不能反序列化
    1. 子类中实现了serializable 接口, 但父类中没有实现, 父类中的变量不能被序列化, 序列化后父类中的变量会得到 null。
    2. 被关键字 transient 修饰的变量不能被序列化。
    3. 静态变量不能被序列化, 属于类, 不属于方法和对象, 所以不能被序列化。

储存 DataSet

  • 将 DataSet 存储为 parquet 文件。
  • 将 DataSet 存储到 JDBC 数据库。
  • 将DataSet 存储到 Hive 表

自定义函数 UDP 和 UDAF

  • UDF(User Defined Function): 用户自定义函数

    package com.ronnie.java.udf_udaf;
    
    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.Dataset;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.RowFactory;
    import org.apache.spark.sql.SparkSession;
    import org.apache.spark.sql.api.java.UDF2;
    import org.apache.spark.sql.types.DataTypes;
    import org.apache.spark.sql.types.StructField;
    import org.apache.spark.sql.types.StructType; import java.util.ArrayList;
    import java.util.Arrays; public class UDF {
    public static void main(String[] args) {
    SparkSession sparkSession = SparkSession.builder().appName("udf").master("local").getOrCreate(); JavaSparkContext jsc = new JavaSparkContext(sparkSession.sparkContext()); JavaRDD<String> parallelize = jsc.parallelize(Arrays.asList("atme", "maybe", "chalice")); JavaRDD<Row> rowRDD = parallelize.map(new Function<String, Row>() { private static final long serialVersionUID = 1L; @Override
    public Row call(String s) throws Exception {
    return RowFactory.create(s);
    }
    }); /**
    * 动态创建Schema方式加载DF
    */ ArrayList<StructField> fields = new ArrayList<>();
    fields.add(DataTypes.createStructField("name", DataTypes.StringType,true)); StructType schema = DataTypes.createStructType(fields); Dataset<Row> df = sparkSession.createDataFrame(rowRDD, schema); df.registerTempTable("user"); /**
    * 根据UDF函数参数的个数来决定是实现哪一个UDF UDF1,UDF2。。。。UDF1xxx
    */ sparkSession.udf().register("StrLen", new UDF2<String, Integer, Integer>(){ private static final long serialVersionUID = 1L; @Override
    public Integer call(String t1, Integer t2) throws Exception {
    return t1.length() + t2;
    }
    }, DataTypes.IntegerType);
    sparkSession.sql("select name ,StrLen(name,100) as length from user").show();
    /**
    * +-------+------+
    * | name|length|
    * +-------+------+
    * | atme| 104|
    * | maybe| 105|
    * |chalice| 107|
    * +-------+------+
    */ sparkSession.stop();
    }
    }
  • UDAF(User Defined Aggregate Function): 用户自定义聚合函数

    • 实现UDAF函数如果要自定义类要实现UserDefinedAggregateFunction

      package com.ronnie.java.udf_udaf;
      
      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.Dataset;
      import org.apache.spark.sql.Row;
      import org.apache.spark.sql.RowFactory;
      import org.apache.spark.sql.SparkSession;
      import org.apache.spark.sql.expressions.MutableAggregationBuffer;
      import org.apache.spark.sql.expressions.UserDefinedAggregateFunction;
      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; import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List; public class UDAF { public static void main(String[] args) {
      SparkSession sparkSession = SparkSession.builder().appName("udaf").master("local").getOrCreate(); JavaSparkContext sc = new JavaSparkContext(sparkSession.sparkContext()); JavaRDD<String> parallelize = sc.parallelize(
      Arrays.asList("zeus", "lina", "wind ranger", "zeus", "zeus", "lina","zeus", "lina", "wind ranger", "zeus", "zeus", "lina"),2); JavaRDD<Row> rowRDD = parallelize.map(new Function<String, Row>() {
      @Override
      public Row call(String s) throws Exception {
      return RowFactory.create(s);
      }
      }); List<StructField> fields = new ArrayList<>();
      fields.add(DataTypes.createStructField("name", DataTypes.StringType, true)); StructType schema = DataTypes.createStructType(fields); Dataset<Row> df = sparkSession.createDataFrame(rowRDD, schema); df.registerTempTable("user"); /**
      * 注册一个UDAF函数,实现统计相同值得个数
      * 注意:这里可以自定义一个类继承UserDefinedAggregateFunction类也是可以的
      * 数据:
      * zeus
      * zeus
      * lina
      * lina
      *
      * select count(*) from user group by name
      */ sparkSession.udf().register("StringCount", new UserDefinedAggregateFunction() { /**
      * 指定输入字段的字段及类型
      * @return
      */
      @Override
      public StructType inputSchema() {
      return DataTypes.createStructType(Arrays.asList(DataTypes.createStructField("name", DataTypes.StringType, true)));
      } /**
      * 指定UDAF函数计算后返回的结果类型
      * @return
      */
      @Override
      public DataType dataType() {
      return DataTypes.IntegerType;
      } /**
      * 确保一致性 一般用true,用以标记针对给定的一组输入,UDAF是否总是生成相同的结果。
      * @return
      */
      @Override
      public boolean deterministic() {
      return true;
      } /**
      * 更新 可以认为一个一个地将组内的字段值传递进来 实现拼接的逻辑
      * buffer.getInt(0)获取的是上一次聚合后的值
      * 相当于map端的combiner,combiner就是对每一个map task的处理结果进行一次小聚合
      * 大聚和发生在reduce端.
      * 这里即是:在进行聚合的时候,每当有新的值进来,对分组后的聚合如何进行计算
      * @param buffer
      * @param input
      */
      @Override
      public void update(MutableAggregationBuffer buffer, Row input) {
      buffer.update(0, buffer.getInt(0) + 1);
      System.out.println("update......buffer" + buffer.toString() + " | row" + input);
      } /**
      * 在进行聚合操作的时候所要处理的数据的结果的类型
      * @return
      */
      @Override
      public StructType bufferSchema() {
      return DataTypes.createStructType(Arrays.asList(DataTypes.createStructField("buffer", DataTypes.IntegerType, true)));
      } /**
      * 合并 update操作,可能是针对一个分组内的部分数据,在某个节点上发生的 但是可能一个分组内的数据,会分布在多个节点上处理
      * 此时就要用merge操作,将各个节点上分布式拼接好的串,合并起来
      * buffer1.getInt(0) : 大聚合的时候 上一次聚合后的值
      * buffer2.getInt(0) : 这次计算传入进来的update的结果
      * 这里即是:最后在分布式节点完成后需要进行全局级别的Merge操作
      */
      @Override
      public void merge(MutableAggregationBuffer buffer1, Row buffer2) {
      /* 2 3 4 5 6 7
      0 + 2 = 2
      2 + 3 = 5
      5 + 4 = 9 */ buffer1.update(0, buffer1.getInt(0) + buffer2.getInt(0));
      System.out.println("merge.....buffer : " + buffer1.toString() + "| row" + buffer2.toString());
      } /**
      * 初始化一个内部的自己定义的值,在Aggregate之前每组数据的初始化结果
      * @param buffer
      */
      @Override
      public void initialize(MutableAggregationBuffer buffer) {
      buffer.update(0, 0); System.out.println("init ......" + buffer.get(0));
      } /**
      * 最后返回一个和DataType的类型要一致的类型,返回UDAF最后的计算结果
      * @param row
      * @return
      */
      @Override
      public Object evaluate(Row row) {
      return row.getInt(0);
      }
      }); sparkSession.sql("select name, StringCount(name) as number from user group by name").show();
      /**
      * +-----------+------+
      * | name|number|
      * +-----------+------+
      * |wind ranger| 2|
      * | lina| 4|
      * | zeus| 6|
      * +-----------+------+
      */ sc.stop();
      }
      }

开窗函数

package com.ronnie.java.windowFun;

import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession; /**
* row_number()开窗函数:
* 主要是按照某个字段分组,然后取另一字段的前几个的值,相当于 分组取topN
* row_number() over (partition by xxx order by xxx desc) xxx
*
*/
public class RowNumberWindowFun {
//-Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.appName("window")
.master("local")
//开启hive的支持,接下来就可以操作hive表了
// 前提需要是需要开启hive metastore 服务
.enableHiveSupport()
.getOrCreate(); sparkSession.sql("use spark");
sparkSession.sql("drop table if exists sales");
sparkSession.sql("create table if not exists sales (riqi string,leibie string,jine Int) "
+ "row format delimited fields terminated by '\t'");
sparkSession.sql("load data local inpath './data/sales.txt' into table sales");
/**
* 开窗函数格式:
* 【 row_number() over (partition by XXX order by XXX) as rank】
* 注意:rank 从1开始
*/
/**
* 以类别分组,按每种类别金额降序排序,显示 【日期,种类,金额】 结果,如:
*
* 1 A 100
* 2 B 200
* 3 A 300
* 4 B 400
* 5 A 500
* 6 B 600
*
* 排序后:
* 5 A 500 --rank 1
* 3 A 300 --rank 2
* 1 A 100 --rank 3
* 6 B 600 --rank 1
* 4 B 400 --rank 2
* 2 B 200 --rank 3
*
* 2018 A 400 1
* 2017 A 500 2
* 2016 A 550 3
*
*
* 2016 A 550 1
* 2017 A 500 2
* 2018 A 400 3
*
*/ Dataset<Row> result = sparkSession.sql("select riqi,leibie,jine,rank "
+ "from ("
+ "select riqi,leibie,jine,"
+ "row_number() over (partition by leibie order by jine desc) rank "
+ "from sales) t "
+ "where t.rank<=3"); result.show(100);
/**
* 将结果保存到hive表sales_result
*/
// result.write().mode(SaveMode.Overwrite).saveAsTable("sales_result");
sparkSession.stop();
}
}

Spark SQL 笔记的更多相关文章

  1. Spark SQL笔记——技术点汇总

    目录 概述 原理 组成 执行流程 性能 API 应用程序模板 通用读写方法 RDD转为DataFrame Parquet文件数据源 JSON文件数据源 Hive数据源 数据库JDBC数据源 DataF ...

  2. Spark SQL笔记

    HDFS HDFS架构 1.Master(NameNode/NN) 对应 N个Slaves(DataNode/NN)2.一个文件会被拆分成多个块(Block)默认:128M例: 130M ==> ...

  3. Hive sql & Spark sql笔记

    记录了日常使用时遇到的特殊的查询语句.不断更新- 1. SQL查出内容输出到文件 hive -e "...Hive SQL..." > /tmp/out sparkhive ...

  4. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  5. Spark SQL官网阅读笔记

    Spark SQL是Spark中用于结构化数据处理的组件. Spark SQL可以从Hive中读取数据. 执行结果是Dataset/DataFrame. DataFrame是一个分布式数据容器.然而D ...

  6. Spark2.x学习笔记:Spark SQL程序设计

    1.RDD的局限性 RDD仅表示数据集,RDD没有元数据,也就是说没有字段语义定义. RDD需要用户自己优化程序,对程序员要求较高. 从不同数据源读取数据相对困难. 合并多个数据源中的数据也较困难. ...

  7. Spark2.x学习笔记:Spark SQL的SQL

    Spark SQL所支持的SQL语法 select [distinct] [column names]|[wildcard] from tableName [join clause tableName ...

  8. Spark2.x学习笔记:Spark SQL快速入门

    Spark SQL快速入门 本地表 (1)准备数据 [root@node1 ~]# mkdir /tmp/data [root@node1 ~]# cat data/ml-1m/users.dat | ...

  9. Spark 学习笔记:(三)Spark SQL

    参考:https://spark.apache.org/docs/latest/sql-programming-guide.html#overview http://www.csdn.net/arti ...

随机推荐

  1. leetcode295 Find Median from Data Stream

    """ Median is the middle value in an ordered integer list. If the size of the list is ...

  2. Xshell 5的快捷键

    Xshell 5的快捷键 1. 点击下图中的按钮查看快捷键: 2. 快捷键备忘录: 序号 功能 快捷键 备注 1 在窗口和撰写栏之间切换 Alt+I   2 全屏 Alt+Enter   3     ...

  3. windows10安装.netframework3.5

    先挂载,看看挂载到哪个盘了,假设是I盘 然后按住shift 点鼠标右键,打开powershell,运行下面命令: dism.exe /online /enable-feature /featurena ...

  4. Redis 一些基本的概念和基础

    Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久 ...

  5. ios 获取当前系统时间

    1. NSDate + NSDateFormatter NSDate *date = [NSDate date]; NSDateFormatter *format = [[NSDateFormatte ...

  6. 每个项目中,你必须知道的11个Java第三方类库。

    Java第三方library ecosystem是一个很广阔的范畴.不久前有人撰文:每个项目中,你必须知道的11个Java第三方类库. 单元测试 1.DBUnit DBunit是一个基于junit扩展 ...

  7. MVC5仓库管理系统

    下载

  8. HDU5943 Kingdom of Obsession 题解

    题意 有 \(n\) 个数 \(s+1\ldots s+n\),求是否能将这 \(n\) 个数放到 \(1\ldots n\) 上,且当令原数为 \(x\),放到 \(y\) 位置时有 \(x \mo ...

  9. c基本语法介绍

    c语言基本语法介绍 1.把常量定义为大写字母形式,是一个很好的编程实践.

  10. mysql 子查询问题

    今天在做子查询的时候发现运行报错, 我的代码是select* from (....) device des ,我一直以为的是device是表名,然后dec是别名,后面问了同事才知道from(...)这 ...