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. <每日一课学习笔记> "mysql为什么加了索引还是慢查询"

    long_query_time mysql判断慢查询的依据是,sql执行时间与系统参数 long_query_time 作比较,如果大于这个参数,就会将这条sql计入慢查询语句中.long_query ...

  2. Melodic 使用URDF创建简单的机器人模型

    本人Linux版本:Ubuntu 18.04LTS ROS版本:Melodic URDF代码 <?xml version="1.0" ?> <robot name ...

  3. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 按钮:原始按钮样式(未被操作)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. jQuery Validation Engine(三) 基本常识

    1:response.validateFail(fieldId, "机构英文名已被其他人使用"); //field为这个字段的id,”“ 双引号的内容,是提示语 <!DOCT ...

  5. Adroid ViewPage+GridView实现每页6个元素,三页滑动切换

    //}//public class MainActivity extends Activity {// private static final float APP_PAGE_SIZE = 16.0f ...

  6. ZB本地设置

    (1)web.config <!--本地服务器--> <add name="connectionString" connectionString="mG ...

  7. 自定义sort排序

    java的sort自定义: 1.排序对象必须是封装类而不能是基本数据类型: 2.调用Arrays.sort(array, left, right, cmp)进行排序,array为数组,left.rig ...

  8. 2017 北京网络赛 E Cats and Fish

    Cats and Fish 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are many homeless cats in PKU campus. They ...

  9. JuJu团队12月27号工作汇报

    JuJu团队12月27号工作汇报 JuJu   Scrum 团队成员 今日工作 剩余任务 困难 飞飞 完成data processing 待安排 无 婷婷 调试代码 提升acc 无 恩升 修正eval ...

  10. arm linux 移植 ffmpeg 库 + x264

    背景 Ffmpeg 中带有h264的解码,没有编码,需要添加x264.libx264是一个自由的H.264编码库,是x264项目的一部分,使用广泛,ffmpeg的H.264实现就是用的libx264. ...