SparkSQL

**

1.SparkSQL*

1. SparkSQL介绍

Spark SQL产生的根本原因时其完全脱离了Hive的限制

SparkSQL支持查询原生的RDD。RDD时Spark平台的核心概念,是Spark能够高效的处理大数据的各种场景的基础
能够在Scala中写SQL。支持简单的SQL语法检查,能够在Scala中写Hive语句访问Hive数据,并将结果取回作为RDD使用

2. Sprak on Hive & Hive on Spark

Sprak on Hive:Hive只作为存储角色,Spark负责Sql解析优化,执行。
Hive on Spark: Hive即作为存储又负责SQL的解析优化,Spark负责执行。

3.Dataset&DataFrame

Dataset是一个分布式数据容器

底层封装的是RDD,当RDD的泛型是Row类型的时候,我们也可以称它为DataFrame。即Dataset=DataFrame
4.SparkSQL数据源

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

5. SparkSQL底层架构

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

**

2. 创建DataSet的几种方式

**
1. 读取Json格式的文件创建DataSet
注意:

json格式中的json数据不能嵌套json格式数据
DataRDD是一个个Row类型的RDD
可以两种方式读取json格式文件
df.show()默认显示前20行数据
Dataset原生API可以操作Dataset(不方便)
注册成临时表时,表中的列默认按ascii顺序显示列

public static void main(String[] args) {
    SparkSession sparkSession = SparkSession.builder().appName("jsonFile").master("local").getOrCreate();

    Dataset<Row> json = sparkSession.read().format("json").load("data/json");

    json.show();

    json.show(4);

    树形的显示Schema信息
    json.printSchema();

  select name,age+100 as addage from table
    json.select(json.col("name"), json.col("age").plus(100).alias("addage")).show();
    select name ,age from table where age >10
    json.select(json.col("name"),json.col("age")).where(json.col("age").gt(19)).show();
    select age,count(*)from table group by age
    json.groupBy(json.col("age")).count().show();


    json.createOrReplaceTempView("jsonSession");

    Dataset<Row> sql = sparkSession.sql("select age ,count(*) as gg from jsonSession group by age");

    sql.show();
    
    sparkSession.stop();

2. 通过json格式的RDD创建DataSet

   SparkSession sparkSession = SparkSession.builder()
            .master("local").appName("jsonRDD").getOrCreate();

    SparkContext sc = sparkSession.sparkContext();

    JavaSparkContext jsc = new JavaSparkContext(sc);

    JavaRDD nameRDD = jsc.parallelize(Arrays.asList(
            "{'name':'zhangsan','age':\"18\"}",
            "{\"name\":\"lisi\",\"age\":\"19\"}",
            "{\"name\":\"wangwu\",\"age\":\"20\"}"
    ));

    JavaRDD<String> scoreRDD = jsc.parallelize(Arrays.asList(
            "{\"name\":\"zhangsan\",\"score\":\"100\"}",
            "{\"name\":\"lisi\",\"score\":\"200\"}",
            "{\"name\":\"wangwu\",\"score\":\"300\"}"
    ));

    Dataset name = sparkSession.read().json(nameRDD);
    Dataset<Row> score = sparkSession.read().json(scoreRDD);

    name.createOrReplaceTempView("nameTable");
    score.createOrReplaceTempView("scoreTable");

    Dataset<Row> sql = sparkSession.sql("select nameTable.name,nameTable.age,scoreTable.score" +
            " from nameTable join scoreTable" +
            " on nameTable.name = scoreTable.name");


    sql.show();

    sc.stop();

3. 非json格式的RDD创建DataSet

  1. 通过反射的方式将json格式的RDD转换成DataSet

自定义类有可序列化
自定义类的访问级别时Public
RDD转换DataSet后会根据映射将字段按Assici码排序
将DataSet转换成RDD时获取字段的两种方式,一种是 getInt(0)下标获取,另一种是getAs(“列名”)获取

   SparkSession sparkSession = SparkSession.builder().appName("reflect").master("local").getOrCreate();
    SparkContext sc = sparkSession.sparkContext();

    JavaSparkContext jsc = new JavaSparkContext(sc);

    JavaRDD<String> lineRDD = jsc.textFile("data/person.txt");

    JavaRDD<Person> map = lineRDD.map(new Function<String, Person>() {
        private static final long serialVersionUID = 1L;

        @Override
        public Person call(String v1) throws Exception {
            Person ps = new Person();
            ps.setId(v1.split(",")[0]);
            ps.setName(v1.split(",")[1]);
            ps.setAge(Integer.valueOf(v1.split(",")[2]));

            return ps;
        }
    });


    Dataset<Row> dataFrame = sparkSession.createDataFrame(map, Person.class);

    dataFrame.show();

    dataFrame.printSchema();

    dataFrame.registerTempTable("person");

    Dataset<Row> sql = sparkSession.sql("select id,name,age from person where id > 2");

    sql.show();


    /*
    * 将DataSet转为JavaRDD
    *
    * */

    final JavaRDD<Row> rowJavaRDD = dataFrame.javaRDD();

    JavaRDD<Person> map1 = rowJavaRDD.map(new Function<Row, Person>() {
        private static final long serialVersionUID = 1L;

        @Override
        public Person call(Row v1) throws Exception {

            Person ps = new Person();

// ps.setName(v1.getAs(“name”));
// ps.setAge(v1.getAs(“age”));
// ps.setId(v1.getAs(“id”));

            ps.setId(v1.getString(2));
            ps.setName(v1.getString(1));
            ps.setAge(v1.getInt(0));


            return ps;
        }
    });
    map1.foreach(new VoidFunction<Person>() {
        @Override
        public void call(Person person) throws Exception {
            System.out.println(person);
        }
    });

    sparkSession.stop();
  1. 动态创建Schema将非json格式的RDD转换位DataSet

     SparkSession sparkSession = SparkSession.builder()
             .master("local").appName("struct").getOrCreate();
     SparkContext sc = sparkSession.sparkContext();
    
     JavaSparkContext jsc = new JavaSparkContext(sc);
    
     JavaRDD<String> lineRDD = jsc.textFile("data/person.txt");
    
     JavaRDD<Row> map = lineRDD.map(new Function<String, Row>() {
    
         private static final long serialVersionUID = 1L;
    
         @Override
         public Row call(String v1) throws Exception {
    
             Row row = RowFactory.create(
                     v1.split(",")[0],
                     v1.split(",")[1],
                     Integer.valueOf(v1.split(",")[2])
             );
             return row;
         }
     });
    
    
     List<StructField> structFields = Arrays.asList(
             DataTypes.createStructField("id", DataTypes.StringType, true),
             DataTypes.createStructField("name", DataTypes.StringType, true),
             DataTypes.createStructField("age", DataTypes.IntegerType, true)
     );
     StructType schema = DataTypes.createStructType(structFields);
    
     Dataset<Row> dataFrame = sparkSession.createDataFrame(map, schema);
    
     dataFrame.printSchema();
    
     dataFrame.show();
    
     sparkSession.stop();
    

4.读取parquet文件创建DataSet
SaveMode指定文件保存时的模式:

Overwrite:覆盖
Append:追加
ErrorIfExists:如果存在就报错
Ignore:如果存在就忽略

     SparkSession sparkSession = SparkSession.builder().appName("parquer").master("local").getOrCreate();
    Dataset<Row> json = sparkSession.read().json("data/json");

    json.show();

    json.write().mode(SaveMode.Overwrite).format("parquet").save("data/parquet");

    Dataset<Row> parquet = sparkSession.read().parquet("data/parquet");

    parquet.show();

    sparkSession.stop();
  1. 读取JDBC中的数据创建DataSet

      SparkSession sparkSession = SparkSession.builder().master("local").appName("mysql").getOrCreate();
     /*
      * 第一种方式读取MySql数据库表,加载为Dataset
      */
     Map<String, String> options = new HashMap<String, String>();
     options.put("url", "jdbc:mysql://127.0.0.1:3306/spark");
     options.put("driver", "com.mysql.jdbc.Driver");
     options.put("user", "root");
     options.put("password", "root");
     options.put("dbtable", "person");
    
     Dataset<Row> person = sparkSession.read().format("jdbc").options(options).load();
    
     person.show();
    
     person.createOrReplaceTempView("person");
    
     /*
     * 第二种方式
     *
     * */
     DataFrameReader reader = sparkSession.read().format("jdbc");
     reader.option("url", "jdbc:mysql://127.0.0.1:3306/spark");
     reader.option("driver", "com.mysql.jdbc.Driver");
     reader.option("user", "root");
     reader.option("password", "root");
     reader.option("dbtable", "score");
    
     Dataset<Row> score = reader.load();
    
     score.show();
    
     score.createOrReplaceTempView("score");
    
     Dataset<Row> sql = sparkSession.sql("select person.id,person.name,person.age,score.score " +
             " from person,score" +
             " where person.name = score.name and score >50");
     sql.show();
    
     sql.createOrReplaceTempView("result");
    
     Dataset<Row> sql1 = sparkSession.sql("select id,name,age,score from result where age <15");
    
     sql1.show();
    
     /*
     * 将Dataset结果保存到Mysql中
     * */
     Properties properties = new Properties();
     properties.setProperty("user","root");
     properties.setProperty("password","root");
    
     sql.write().mode(SaveMode.Overwrite).jdbc("jdbc:mysql://127.0.0.1:3306/spark", "result12",properties);
    
     System.out.println("已加入数据库");
    
     sparkSession.stop();
    
  2. 读取Hive中的数据加载成DataSet

      SparkSession sparkSession = SparkSession.builder().appName("hive").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> sql = 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");
     sql.registerTempTable("good");
    
     Dataset<Row> sql1 = sparkSession.sql("selelct * from good");
    
     sql1.show();
    
     /**
      * 将结果保存到hive表 good_student_infos
      */
     sparkSession.sql("DROP TABLE IF EXISTS good_student_infos");
    
     sql1.write().mode(SaveMode.Overwrite).saveAsTable("good_student_infos");
    
     sparkSession.stop();
    
  3. 序列化

  4. 反序列化时 实体类中的serialVersionUID 改变后能否反序列化?(不能)
    Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时, JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。 当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时, Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。

  5. 子类实现serializable 父类不实现serializable,当序列化子类时,反序列化得到父类中的某变量的数值时,该变量的数值与序列化时的数值不相同

一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象,然后反序列化后输出父类定义的某变量的数值,
该变量数值与序列化时的数值不同。(需要在父类中是实现默认的构造方法,否则会报异常:no validconstructor)
在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。

  1. 静态变量能否被序列化?(不能被序列化)
    当改变静态变量的值时,序列化后的静态变量也是改变的,如果正常能被序列化的值,是在反序列化中得到的值是不会变的,可见,静态变量不能被序列化。
    那为什么反序列化可以读到值呢? 一个静态变量不管是否被transient修饰,均不能被序列化,反序列化后类中static型变量isPerson的值为当前JVM中对应static变量的值,这个值是JVM中的,不是反序列化得出的。
  2. UDF&UDAF
  3. UDF:用户自定义函数
  4. UDAF: 用户自定义聚合函数

initialize:初始化
初始化一个内部自己定义的值,,在Aggregate之前每组数据的初始化结果。
update:更新
一个个的将组内的字段传递进来,实现拼接的逻辑
buffer.getInt(0)获取的是上一次聚合后的值
marge:合并update
将各个节点上分布式拼接好的值,合并起来。
buffer1.getInt(0) : 大聚合的时候 上一次聚合后的值
buffer2.getInt(0) : 这次计算传入进来的update的结果
StructType bufferSchema
在进行聚合操作的时候所要处理的数据的结果的类型
evaluate
最后返回一个和DataType的类型要一致的类型,返回UDAF最后的计算结果
dataType
指定UDAF函数计算后返回的结果类型
inputSchema
指定输入字段的字段及类型
deterministic
确保一致性 一般用true,用以标记针对给定的一组输入,UDAF是否总是生成相同的结果。

  1. 开窗函数

row_number() 开窗函数是按照某个字段分组,然后取另一字段的前几个的值,相当于 分组取topN。
row_number() over (partitin by XXX order by XXX)

转载自 链接:https://www.jianshu.com/p/ac0feb1baa85

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值