[Spark]-结构化数据查询之初识篇

1.概述

  Spark-SQL是Spark用于处理结构化数据的一个模块.

  它与RDD的优势在于

    对外而言,类SQL语言会有更大的人群适应性.

    对内而言,Spark-SQL的结构化信息,会带来额外的执行优化

  注意一个认识误区,Spark-SQL 不等于 SQL.

    Spark-SQL是Spark用于处理结构化数据的一个模块,SQL仅仅只是Spark-SQL中一部分,而在SQL之外,Spark还有DataFrame,DataSet等等,也是Spark用来处理结构化数据的技术手段

2.Spark的结构化数据处理方案

  2.1 dataset

     dataset是Spark自1.6+版本后添加一种新的分布式数据集.类似与一个强类型的分布式的Java集合

     它的目的为传统RDD增加结构化(强类型)的支持,依赖更多的来自结构化的信息,为执行计划提供更好的优化.

   2.2 dataframe

    dataframe是dataset<Row>的别名,它是用统一的Row对象(包含列名,数据类型等等)作为强类型的包装,类似与关系型数据库的表概念.

    dataframe相比dataset,在面对外部数据源时会更加方便(不用写强类型定义),比如结构化数据文件等等

  2.3 SQL

    Spark对结构化查询语言(SQL)处理结构化数据的支持.并且完美兼容HiveSQL(事实上,Spark甚至内置了一个Hive引擎)

  这三种只是针对开发者而言的技术手段区分,事实上Spark内部是使用同一种计算引擎处理的.换句话说,我们可以在这三种技术手段之间自由的切换

2.SparkSession

  SparkSession 是 Spark-SQL的统一入口.一个简单的SparkSession声明如下  

  import org.apache.spark.sql.SparkSession

  val spark = SparkSession
    .builder()
    .getOrCreate()

  // For implicit conversions like converting RDDs to DataFrames
  import spark.implicits._

3.DataFrame

 

import org.apache.spark.sql.SparkSession;

  val spark = SparkSession
    .builder()
    .appName("Spark-Sql-DataFrame-App").master("local[2]")
    .getOrCreate();
  import spark.implicits._;

  /**
    * 构建一个DF
    *   读取一个本地Json文件(可以是HDFS或任意Hadoop支持的存储系统)
    */
  val peopleDF =  spark.read.json("D:\\data\\people.json");

  /**
    * DF的一些基本操作
    */
  println("******************************** DF的一些基本操作 *************************************")
  //打印架构
  peopleDF.printSchema()
  //打印前10行数据
  peopleDF.show(10)
  //dataFrame依然有RDD的支持(比如持久化机制)
  val peopleCache = peopleDF.filter($"age">0).cache();
  //一个简单的dataFrame查询实例
  peopleCache.filter($"age"< 30).select($"name",$"age").groupBy($"name",$"age").count().show(10)


  /**
    * DF转SQL的临时表
    * 这样可以非常容易的将一个DF转到SQL层面来处理
    */
  println("******************************** DF转SQL *************************************")
  //类似RDBMS的会话临时表,SparkSession关闭后自动关闭临时表
  peopleCache.createOrReplaceTempView("peopleTmp")
  spark.sql("select name,age,count(1) from peopleTmp where age < 30 group by name,age").show(10)
  //类似RDBMS的全局临时表,可以在跨Session状态下使用
  //全局临时表必须注册在global_temp,且使用时必须包含全名: global_temp.xxxx
  peopleCache.createOrReplaceGlobalTempView("peopleGlobTmp")
  spark.newSession().sql("select name,age,count(1) from global_temp.peopleGlobTmp where age < 30 group by name,age").show(10)

  spark.close();

 

 4.Dataset 

 

  import org.apache.spark.sql.SparkSession;

  val spark = SparkSession
    .builder()
    .appName("Spark-Sql-DataSet-App").master("local[2]")
    .getOrCreate();

  import spark.implicits._;

  //定义描述DS结构的Person类型
  case class Person(name: String, age: Long)

  /**
    * 构建DS
    */
  //从一个Java对象集合中创建DS
  val dsFromScalaCollection = Seq(Person("SuperMan", 20)).toDS();
  //从一个结构化的外部系统中创建DS
  val dsFromLocalSystem_Json = spark.read.format("json").load("D:\\data\\people.json").na.fill(0,Seq("age")).as[Person];
  //从一个非结构化的外部数据源中创建DS
  val dsFromLocalSystem_Txt = spark.read.textFile("D:\\data\\people.txt")
    .map(line => line.split(",")).map(columns => Person(columns(0), columns(1).trim.toInt))
  //使用unionByName而不使用union  union:根据模式中的位置解析列(模式中的列位置不一定与数据集中强类型对象中的字段匹配)
  val peopleDS = dsFromScalaCollection.unionByName(dsFromLocalSystem_Txt).unionByName(dsFromLocalSystem_Json)

  //数据任意使用,不关心来自什么外部系统(外部系统异构透明)
  peopleDS.filter($"age"< 30).select($"name",$"age").groupBy($"name",$"age").count().show(10)

  spark.stop()

 

5.Sql

 

  val spark = SparkSession.builder().appName("SQL-App").master("local[2]").getOrCreate()
  import  spark.implicits._;
  case class Person(name: String, age: Long)

  //DS读取非结构化数据至SQL临时表
  spark.read.textFile("D:\\data\\people.txt")
    .map(line=>line.split(",")).map(columns=>Person(columns(0),columns(1).trim.toLong))
    .createTempView("data_from_txt")

  //Sql方式读取数据
  val sql =
    """
      |CREATE TEMPORARY VIEW data_from_json
      |USING org.apache.spark.sql.json
      |OPTIONS (
      |  path "D:\\data\\people.json"
      |)
    """.stripMargin;
  spark.sqlContext.sql(sql);
  //简单使用
  spark.sqlContext.sql("SELECT * FROM  data_from_json json join data_from_txt txt on json.name = txt.name").show();

  spark.close();

 

6.Spark-SQL与RDD

  Spark-SQL支持两种方式将一个RDD转换为Dataset.

    使用反射的方式去从一个包含指定类型的RDD中,推导出指定类型的schema

    手动构造一个shema,然后将它应用在一个已存在的编程接口.(这种方式比较繁琐,但这提供了对列和类型都是未知的情况下构造一个Dataset的一个途径)

  6.1 反射推导schema   

        val spark = SparkSession.builder().master("local[2]").appName("RDD2DFDS_App").getOrCreate();
        
        import spark.implicits._;
        
        //反射推导需要定义DS的目标类型
        case class Person(name: String, age: Long)
        //构造出一个RDD
        val rdd = spark.sparkContext.textFile("D:\\data\\people.txt")
        //RDD TO DF
        val df = rdd
        .map(row => row.split(","))
        .map(columns => Person(columns(0), columns(1).trim.toInt))
        .toDF()
        df.show(10)
        
        spark.close()

  6.2 编程方式写入schema

    当类型无法在执行之前定义时,Dataframe可以按照以下步骤构建出:

      从原始RDD中构建出行(Row)定义

      创建一个StructType(schema的定义),匹配刚才定义的行(Row)的结构

      使用SparkSession提供的createDataFrame方法应用 Schema 到 RDD 的行(Row)  

        val spark = SparkSession.builder().master("local[2]").appName("RDD2DFDS_App").getOrCreate();
        import spark.implicits._;
        
        //一个非结构化的原始RDD
        val rdd = spark.sparkContext.textFile("D:\\data\\people.txt")
        //原始RDD转RDD[Row]
        val rowRdd = rdd.map(row=>row.split(",")).map(columns=>Row(columns(0),columns(1).trim.toLong))
        //定义schema
        val schema = StructType(
        StructField(name = "name", dataType = org.apache.spark.sql.types.StringType, nullable = true) ::
        StructField(name = "age", dataType = org.apache.spark.sql.types.LongType, nullable = true) :: Nil
        )
        // DF 等于 RDD[Row] + schema
        val df =spark.createDataFrame(rowRdd,schema)
        df.show();
        spark.close()

 

posted @ 2018-07-04 00:41  NightPxy  阅读(907)  评论(0编辑  收藏  举报