[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()