SparkSQL总结

一.SparkSQL介绍:

    1.简单来说,Hive是Shark的前身,Shark则是SparkSQL的前身,SparkSQL产生的原因则是由于Shark对于Hive的太多依赖(如采用Hive的语法解析器、查询优化器等等),制约了Spark的One Stack Rule Them All的既定方针,制约了Spark各个组件的相互集成。SparkSQL抛弃了原有Shark的代码,汲取了Shark的一些优点,如内存列存储(In-Memory Columnar Storage)、Hive兼容性等,重新开发了SparkSQL代码;由于摆脱了对Hive的依赖性,SparkSQL无论在数据兼容、性能优化、组件扩展方面都得到了极大的方便。

  • SparkSQL支持查询原生的RDD。 RDD是Spark平台的核心概念,是Spark能够高效的处理大数据的各种场景的基础。

  • 能够在scala,java等开发语言中写SQL语句。支持简单的SQL语法检查,能够在Scala中写Hive语句访问Hive数据,并将结果取回作为RDD使用。

 2. SparkSQL作用:提供一个编程抽象(DataFrame) 并且作为分布式 SQL 查询引擎。

3.SparkSQL运行原理简述:将 Spark SQL 转化为 RDD, 然后提交到集群执行。

4.SparkSQL特点:

   4.1 易整合:将sql查询与spark程序无缝混合,可以使用java、scala、python、R等语言的API操作。

  4.2 统一的数据访问方式:可以用相同的方式连接到任何数据源。

  4.3 兼容Hive

  4.4 标准的数据连接

5.SparkSession:

        SparkSession是Spark 2.0引如的新概念。SparkSession为用户提供了统一的切入点,来让用户学习spark的各项功能。 
  在spark的早期版本中,SparkContext是spark的主要切入点,由于RDD是主要的API,我们通过sparkcontext来创建和操作RDD。对于每个其他的API,我们需要使用不同的context。例如,对于Streming,我们需要使用StreamingContext;对于sql,使用sqlContext;对于Hive,使用hiveContext。但是随着Dataset和DataFrame的API逐渐成为标准的API,就需要为他们建立接入点。所以在spark2.0中,引入SparkSession作为Dataset和DataFrame API的切入点,SparkSession封装了SparkConf、SparkContext和SQLContext。为了向后兼容,SQLContext和HiveContext也被保存下来。 
   
  SparkSession实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext),所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了sparkContext,所以计算实际上是由sparkContext完成的。

特点:

   ---- 为用户提供一个统一的切入点使用Spark 各项功能

        ---- 允许用户通过它调用 DataFrame 和 Dataset 相关 API 来编写程序

        ---- 减少了用户需要了解的一些概念,可以很容易的与 Spark 进行交互

        ---- 与 Spark 交互之时不需要显示的创建 SparkConf, SparkContext 以及 SQlContext,这些对象已经封闭在 SparkSession 中

6.Dataset与DataFrame:

SparkSQL总结_第1张图片

Dataset也是一个分布式数据容器。与RDD类似,然而Dataset更像传统数据库的二维表格,除了数据以外,还掌握数据的结构信息,即schema。同时,与Hive类似,Dataset也支持嵌套数据类型(struct、array和map)。从API易用性的角度上 看, Dataset  API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。

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

7.SparkSQL的数据源:SparkSQL总结_第2张图片

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

二.创建Dataset的几种方式:

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

 代码:

 public static void main(String[] args) {

        SparkSession sparkSession = SparkSession
                .builder()
                .appName("jsonFile")
                .master("local")
                .getOrCreate();
        Dataset ds = sparkSession.read().format("json").load("json");

        ds.show();
    }

json文件:

{"name":"zhangsan","age": 18}
{"name":"lisi"}
{"name":"wangwu","age":18}
{"name":"laoliu","age":28}
{"name":"zhangsan","age":20}
{"name":"lisi"}
{"name":"wangwu","age":18}
{"name":"laoliu","age":28}
{"name":"zhangsan","age":28}
{"name":"lisi"}
{"name":"wangwu","age":18}

输出结果:

SparkSQL总结_第3张图片

注意点:

  • json文件中的json数据不能嵌套json格式数据。
  • Dataset是一个一个Row类型的RDD,ds.rdd()/ds.javaRdd()。
  • 可以两种方式读取json格式的文件。
  •         Dataset ds = sparkSession.read().format("json").load("data/json");
    //		Dataset ds = sqlContext.read().json("data/json");

     

  • df.show()默认显示前20行数据。
  •  /**
       * Displays the top 20 rows of Dataset in a tabular form. Strings more than 20 characters
       * will be truncated, and all cells will be aligned right.
       *
       * @group action
       * @since 1.6.0
       */
      def show(): Unit = show(20)

     

  • Dataset原生API可以操作Dataset(不方便)。
  • 注册成临时表时,表中的列默认按ascii顺序显示列。

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

代码:

 public static void main(String[] args) {

        SparkSession sparkSession = SparkSession
                .builder()
                .appName("jsonRDD")
                .master("local")
                .getOrCreate();
        /**
         * 注意:
         *   1.由于是java版,故通过javaSparkcontext.parallelize来创建json格式的JavaRDD
         *   故我们通过sparkcontext来创建javaSparkcontext
         *   2.如果是scala版,直接通过sparkcontext.parallelize来创建,就无需创建javaSparkcontext
         */
        SparkContext sc = sparkSession.sparkContext();
        JavaSparkContext jsc = new JavaSparkContext(sc);

        JavaRDD nameRDD = jsc.parallelize(Arrays.asList(
                "{'name':'jujingyi','age':\"18\"}",
                "{\"name\":\"zhuxudan\",\"age\":\"19\"}",
                "{\"name\":\"zhouzhiruo\",\"age\":\"20\"}"
        ));
        JavaRDD scoreRDD = jsc.parallelize(Arrays.asList(
                "{\"name\":\"jujingyi\",\"score\":\"100\"}",
                "{\"name\":\"zhuxudan\",\"score\":\"200\"}",
                "{\"name\":\"zhouzhiruo\",\"score\":\"300\"}"
        ));
        Dataset nameds = sparkSession.read().json(nameRDD);
        Dataset scoreds = sparkSession.read().json(scoreRDD);
        //注册为临时表使用
        nameds.createOrReplaceTempView("nameds");
        scoreds.createOrReplaceTempView("scoreds");
        Dataset ds =
                sparkSession
                        .sql("select n.name,n.age,s.score from nameds n join scoreds s where n.name" +
                                "= s.name");
        ds.show();
        sc.stop();

    }

输出结果:

SparkSQL总结_第4张图片

3.非json格式的RDD创建Dataset

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

 代码:

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 lineRDD = jsc.textFile("person.txt");

        JavaRDD map = lineRDD.map(new Function() {
            private static final long serialVersionUID = 1L;

            @Override
            public Person call(String v1) throws Exception {
                Person person = new Person();

                person.setId(v1.split(",")[0]);
                person.setName(v1.split(",")[1]);
                person.setAge(Integer.valueOf(v1.split(",")[2]));
                return person;
            }
        });
        /**
         * 传入进去Person.class的时候,sqlContext是通过反射的方式创建Dataset
         * 在底层通过反射的方式获得Person的所有field,结合RDD本身,生成Dataset
         */
        Dataset dataFrame = sparkSession.createDataFrame(map, Person.class);
        dataFrame.show();
    }

person.txt文件:

1,zhuxudan,18
2,zhaoming,19
3,zhouzhiruo,20
4,jujingyi,34

输出结果:

SparkSQL总结_第5张图片

注意点:

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

3.2 动态创建Schema将非RDD文件转换为Dataset

代码:

public class CreateDSFromStruct {

    public static void main(String[] args) {

        SparkSession sparkSession = SparkSession
                .builder()
                .master("local")
                .appName("struct")
                .getOrCreate();
        SparkContext sc = sparkSession.sparkContext();
        JavaSparkContext jsc = new JavaSparkContext(sc);
        JavaRDD line = jsc.textFile("person.txt");

        JavaRDD map = line.map(new Function() {

            private static final long serialVersionUID = 1L;

            @Override
            public Row call(String s) throws Exception {
                return RowFactory.create(
                        s.split(",")[0],
                        s.split(",")[1],
                        Integer.valueOf(s.split(",")[2])
                );
            }
        });
        /**
         * 动态构建DataFrame中的元数据,一般来说这里的字段可以来源自字符串,也可以来源于外部数据库
         */
        List 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 = sparkSession.createDataFrame(map,schema);
        row.show();
        row.printSchema();
        sc.stop();


    }

}

文件同上:

输出结果:

SparkSQL总结_第6张图片

4. 读取parquet文件创建Dataset:

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

        Dataset df = sparkSession.read().json("json");

//		sparkSession.read().format("json").load("./spark/json");
        df.show();
        /**
         * 将Dataset保存成parquet文件,
         * SaveMode指定存储文件时的保存模式:
         * 		Overwrite:覆盖
         * 		Append:追加
         * 		ErrorIfExists:如果存在就报错
         * 		Ignore:如果存在就忽略
         * 保存成parquet文件有以下两种方式:
         */

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

        /**
         * 加载parquet文件成Dataset
         * 加载parquet文件有以下两种方式:
         */

        Dataset load = sparkSession.read().format("parquet").load("data/parquet");
////		load = sparkSession.read().parquet("data/parquet");
        load.show();
        sparkSession.stop();
    }

   注意点:

  • 可以将Dataset存储成parquet文件。保存成parquet文件的方式有两种

df.write().mode(SaveMode.Overwrite)format("parquet")

                                    .save("./sparksql/parquet");

df.write().mode(SaveMode.Overwrite).parquet("./sparksql/parquet");

  • SaveMode指定文件保存时的模式。

       Overwrite:覆盖

       Append:追加

       ErrorIfExists:如果存在就报错

       Ignore:如果存在就忽略

5.读取jdbc中的数据创建Dataset(以mysql为例)

 public static void main(String[] args) {

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

        /**
         * 第一种方式读取MySql数据库表,加载为Dataset
         */
        Map options = new HashMap();
        options.put("url", "jdbc:mysql://192.168.235.101:3306/goldmantis");
        options.put("driver", "com.mysql.jdbc.Driver");
        options.put("user", "root");
        options.put("password", "xxxxxxxx");
        options.put("dbtable", "sys_staffs");

        Dataset ds = sparkSession.read().format("jdbc").options(options).load();
//        ds.show();
        ds.createOrReplaceTempView("staff");

        /**
         * 第二种方式读取MySql数据表加载为Dataset
         */
        DataFrameReader reader = sparkSession.read().format("jdbc");
        reader.option("url", "jdbc:mysql://192.168.235.101:3306/goldmantis");
        reader.option("driver", "com.mysql.jdbc.Driver");
        reader.option("user", "root");
        reader.option("password", "xxxxxxxx");
        reader.option("dbtable", "site_receiving");

        Dataset receive = reader.load();
        receive.createOrReplaceTempView("receive");

        Dataset result = sparkSession.sql("select s.NAME,s.ACCOUNT_NO,s.ORG_NAME,r.flow_no,r.project_name " +
                ",r.remark  from site_receiving r, sys_staffs s where s.ACCOUNT_NO = r.account_no ");
        result.show();

        result.registerTempTable("result");
        Dataset row = sparkSession.sql("select * from result");
        row.show();
    }

6.读取hive中数据加载成Dataset;

 

你可能感兴趣的:(SparkSQL,SparkSQL,Spark)