Spark SQL基本操作

1 概述

本文介绍spark sql的几种执行方式:SQL,DataFramesAPI与Datasets API(但会使用同一个执行引擎),Spark2.0中引入了SparkSession的概念。该篇文章只是做一个简单的了解,让大家有一个感官性的认识。下一篇会对RDD、DataFrame、Dataset进行一个详细的介绍。

spark sql是为了处理结构化数据的一个spark 模块。不同于spark rdd的基本API,Spark SQL中提供的接口将会提供给Spark更多关于结构化数据和计算的信息。在spark内部,sql sql利用这些信息去更好地进行优化。有如下几种方式执行spark sql:SQL,DataFramesAPI与Datasets API。当相同的计算引擎被用来执行一个计算时,有不同的API和语言种类可供选择。这种统一性意味着开发人员可以来回轻松切换各种最熟悉的API来完成同一个计算工作。

2 spark session

Spark2.0中引入了SparkSession的概念,它为用户提供了一个统一的切入点来使用Spark的各项功能,用户不但可以使用DataFrame和Dataset的各种API,学习Spark2的难度也会大大降低。

在2.0版本之前,使用Spark必须先创建SparkConf和SparkContext,代码如下:

//set up the spark configuration and create contexts
val sparkConf = new SparkConf().setAppName("SparkSessionZipsExample").setMaster("local")

// your handle to SparkContext to access other context like SQLContext
val sc = new SparkContext(sparkConf)
val sqlContext = new org.apache.spark.sql.SQLContext(sc)

Spark2.0中只要创建一个SparkSession就够了,SparkConf、SparkContext和SQLContext都已经被封装在SparkSession当中。下面的代码创建了一个SparkSession对象并设置了一些参数。这里使用了生成器模式,只有此“spark”对象不存在时才会创建一个新对象。

// Create a SparkSession. No need to create SparkContext
// You automatically get it as part of the SparkSession
val warehouseLocation = "file:${system:user.dir}/spark-warehouse"
val spark = SparkSession
.builder()
.appName("Spark SQL basic example")
.config("spark.some.config.option", "some-value")
.enableHiveSupport() //支持hive
.getOrCreate()

执行完上面的代码就可以使用spark对象了。

3 Dataframe

DataFrame是Dataset中一个有名字的列。从概念上,它等价于关系型数据库中的一张表,或者等价于R/Python中的Data Frame,但它在底层做了更好的优化。构造DataFrame的数据源很多:结构化的数据文件、hive表、外部数据库、已经存在的RDD。DataFrame 的API支持java,scal.python,R。

通过SparkSession,应用程序可以从一个现有的RDD、Hive表、Spark数据源来创建一个DataFrame。

首先我们开启一个spark-shell进行测试:

Spark context Web UI available at http://192.168.137.130:4040
Spark context available as 'sc' (master = local[2], app id = local-1524409122149).
Spark session available as 'spark'.
查看启动日志我们会发现上面一句话,sc大家应该不陌生了,这里的spark就是一个SparkSession对象,启动spark-shell默认给我们创建好的。
cat people.json
{"name":"Michael"}
{"name":"Andy", "age":30}
{"name":"Justin", "age":19}

读取json文件(这里也体现了可以读取各种数据结构)
val df=spark.read.json("file:///opt/software/spark-2.2.0-bin-2.6.0-cdh5.7.0/examples/src/main/resources/people.json")
这里就会创建一个DataFrame

df.show
这里的show方法,默认只展示20条记录
+----+-------+
| age|   name|
+----+-------+
|null|Michael|
|  30|   Andy|
|  19| Justin|
+----+-------+

这里我们可以看一看源码来解读这个show方法,是比较简单的

  • show源码解读
 def show(): Unit = show(20)

  /** 只展示20行
   * Displays the top 20 rows of Dataset in a tabular form.
   *
   * @param truncate Whether truncate long strings. If true, strings more than 20 characters will
   *                 be truncated and all cells will be aligned right
   *
   * @group action
   * @since 1.6.0
   */   默认为true ,你可以传递一个false可以展示所有行
  def show(truncate: Boolean): Unit = show(20, truncate)
df.printSchema()
18/04/22 07:07:22 INFO ContextCleaner: Cleaned accumulator 53
18/04/22 07:07:22 INFO ContextCleaner: Cleaned accumulator 57
18/04/22 07:07:22 INFO ContextCleaner: Cleaned accumulator 55
root
 |-- age: long (nullable = true)
 |-- name: string (nullable = true)

列出了他的Schema结构(Tree)

df.select("name").show
+-------+
|   name|
+-------+
|Michael|
|   Andy|
| Justin|
+-------+

df.select("name","age").show 
+-------+----+
|   name| age|
+-------+----+
|Michael|null|
|   Andy|  30|
| Justin|  19|
+-------+----+

df.select(df.col("name")).show
+-------+
|   name|
+-------+
|Michael|
|   Andy|
| Justin|
+-------+

df.select('name).show  
(这里要注意,使用这种写法,在控制台是可以直接使用的,
但是在idea中需要导入:import spark.implicits_ 进行隐式转换否则报错。)
+-------+
|   name|
+-------+
|Michael|
|   Andy|
| Justin|
+-------+

df.select($"name").show
+-------+
|   name|
+-------+
|Michael|
|   Andy|
| Justin|
+-------+

df.select($"name",$"age"+10).show
+-------+----------+
|   name|(age + 10)|
+-------+----------+
|Michael|      null|
|   Andy|        40|
| Justin|        29|
+-------+----------+

df.filter($"age">21).show
+---+----+
|age|name|
+---+----+
| 30|Andy|
+---+----+

df.groupBy("age").count().show
+----+-----+
| age|count|
+----+-----+
|  19|    1|
|null|    1|
|  30|    1|
+----+-----+


  • select源码解读
/**  可以看到select中可以传递多个列,并且可以对列进行四则运算,默认调用的是col方法,
   *  所以这种写法df.select(df.col("name")) 大家应该明白了把。 个人感觉就是各种千奇百怪     的写法都可以哈
   * Selects a set of column based expressions.
   * {{{
   *   ds.select($"colA", $"colB" + 1)
   * }}}
   *
   * @group untypedrel
   * @since 2.0.0
   */
  @scala.annotation.varargs
  def select(cols: Column*): DataFrame = withPlan {
    Project(cols.map(_.named), logicalPlan)
  }

  /**
   * Selects a set of columns. This is a variant of `select` that can only select
   * existing columns using column names (i.e. cannot construct expressions).
   *
   * {{{
   *   // The following two are equivalent:
   *   ds.select("colA", "colB")
   *   ds.select($"colA", $"colB")
   * }}}
   *
   * @group untypedrel
   * @since 2.0.0
   */
  @scala.annotation.varargs
  def select(col: String, cols: String*): DataFrame = select((col +: cols).map(Column(_)) : _*)

  • 使用sql方式生成一个 DataFrame对象
    SparkSession中的SQL函数可以让应用程序以编程的方式运行SQL查询语句,让结果返回一个DataFrame。
// Register the DataFrame as a SQL temporary view
df.createOrReplaceTempView("people")

val sqlDF = spark.sql("SELECT * FROM people")
sqlDF.show()
// +----+-------+
// | age|   name|
// +----+-------+
// |null|Michael|
// |  30|   Andy|
// |  19| Justin|
// +----+-------+

这种方式创建的是一个临时表只在当前application有作用。

全局临时视图
 Spark SQL中的临时视图作用域仅仅在于创建该视图的会话窗口,如果窗口关闭,该视图也终止。
 如果你想要一个在所有会话中都生效的临时视图,并且即使应用程序终止该视图仍然存活,你可以创建一个全局临时视图。
 全局临时视图与系统保存数据库global_temp相关联,我们必须使用规范的名字来定义它,
 比如:SELECT * FROM global_temp.view1.

// Register the DataFrame as a global temporary view
df.createGlobalTempView("people")

// Global temporary view is tied to a system preserved database `global_temp`
spark.sql("SELECT * FROM global_temp.people").show()
// +----+-------+
// | age|   name|
// +----+-------+
// |null|Michael|
// |  30|   Andy|
// |  19| Justin|
// +----+-------+

// Global temporary view is cross-session
spark.newSession().sql("SELECT * FROM global_temp.people").show()
// +----+-------+
// | age|   name|
// +----+-------+
// |null|Michael|
// |  30|   Andy|
// |  19| Justin|
// +----+-------+

个人建议,使用api的方式进行编程,对于一些复杂的sql是很难做出优化的,使用api的方式就会变得简单。

4 Dataset

Dataset是一个分布式数据集合。Dataset是一个在Spark 1.6版本之后才引入的新接口,它既拥有了RDD的优点(强类型、能够使用强大的lambda函数),又拥有Spark SQL的优点(用来一个经过优化的执行引擎)。你可以将一个JVM对象构造成一个Dataset,之后就可以使用一些transformations操作啦。我们可以使用scala,java来访问Dataset API,不支持python哦,当然,由于python的动态特性,很多的Dataset API是可以使用的,R语言也是一样

Dataset有点像RDD,但它并不是使用java或Kryo这样的序列化方式,而是使用专用的编码器将对象进行序列化,以便于在网络上进行处理和传输。虽然编码器和标准的序列化都可以将对象转成字节,但编码器产生动态的代码,它使用的格式允许Spark在不执行反序列化的情况下去执行像过滤、排序、哈希等许许多多的操作。

case class Person(name: String, age: Long)

// Encoders are created for case classes
val caseClassDS = Seq(Person("Andy", 32)).toDS()
caseClassDS.show()
// +----+---+
// |name|age|
// +----+---+
// |Andy| 32|
// +----+---+

// Encoders for most common types are automatically provided by importing spark.implicits._
val primitiveDS = Seq(1, 2, 3).toDS()
primitiveDS.map(_ + 1).collect() // Returns: Array(2, 3, 4)

// DataFrames can be converted to a Dataset by providing a class. Mapping will be done by name
val path = "examples/src/main/resources/people.json"
val peopleDS = spark.read.json(path).as[Person]
peopleDS.show()
// +----+-------+
// | age|   name|
// +----+-------+
// |null|Michael|
// |  30|   Andy|
// |  19| Justin|
// +----+-------+
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值