在前面的 RDD 部分, 非常明显可以感觉的到是命令式的, 主要特征是通过一个 算子, 可以得到一个结果, 通过结果再进行后续计算。
sc.textFile("...")
.flatMap(_.split(" "))
.map((_, 1)) 26 / 110
.reduceByKey(_ + _)
.collect()
命令式的优点
操作粒度更细,能够控制数据的每一个处理环节;
操作更明确,步骤更清晰,容易维护;
支持半/非结构化数据的操作。
命令式的缺点
对于一些数据科学家/数据库管理员/DBA, 要求他们为了做一个非常简单的查询, 写一大堆代码, 明显是一件非常残忍的事情, 所以 SQL on Hadoop 是一个非常 重要的方向。
SELECT name, age, school FROM students WHERE age > 10
SQL 擅长数据分析和通过简单的语法表示查询,命令式操作适合过程式处理和算 法性的处理。
在 Spark 出现之前,对于结构化数据的查询和处理, 一个工具一向只能支持 SQL 或者命令式,使用者被迫要使用多个工具来适应两种场景,并且多个工具配 合起来比较费劲。
而 Spark 出现了以后,统一了两种数据处理范式是一种革新性的进步。
SQL 是数据分析领域一个非常重要的范式,所以 Spark 一直想要支持这种范式, 而伴随着一些决策失误,这个过程其实还是非常曲折的。
(1) 发展历史
Hive
解决的问题: Hive 实现了 SQL on Hadoop,使用 MapReduce 执行任务 简化了 MapReduce 任 务。
新的问题: Hive 的查询延迟比较高,原因是使用 MapReduce 做计算。
Shark
解决的问题:
Shark 改写 Hive 的物理执行计划,使用 Spark 代替 MapReduce 物理引擎 使 用列式内存存储。以上两点使得 Shark 的查询效率很高。
新的问题: Shark 执行计划的生成严重依赖 Hive,想要增加新的优化非常困难; Hive 是进程级别的并行,Spark 是线程级别的并行,所以 Hive 中很多线程不 安全的代码不适用于 Spark; 由于以上问题,Shark 维护了 Hive 的一个分支,并且无法合并进主线,难以为 继;在 2014 年 7 月 1 日的 Spark Summit 上,Databricks 宣布终止对 Shark 的 开发,将重点放到 Spark SQL 上。
SparkSQL-DataFrame
解决的问题: Spark SQL 执行计划和优化交给优化器 Catalyst; 内建了一套简单的 SQL 解析器,可以不使用 HQL; 还引入和 DataFrame 这样的 DSL API,完全可以不依赖任何 Hive 的组件
新的问题: 对于初期版本的 SparkSQL,依然有挺多问题,例如只能支持 SQL 的使用,不能 很好的兼容命令式,入口不够统一等。
SparkSQL-Dataset
SparkSQL 在 1.6 时代,增加了一个新的 API,叫做 Dataset,Dataset 统一和 结合了 SQL 的访问和命令式 API 的使用,这是一个划时代的进步。 在 Dataset 中可以轻易的做到使用 SQL 查询并且筛选数据,然后使用命令式 API 进行探索式分析。
Hive 是将 SQL 转为 MapReduce。 SparkSQL 可以理解成是将 SQL 解析成:“RDD + 优化” 再执行。
一般指数据有固定的 Schema(约束),例如在用户表中,name 字段是 String 型, 那么每一条数据的 name 字段值都可以当作 String 来使用:
一般指的是数据没有固定的 Schema,但是数据本身是有结构的。
Dataset 也可能长这样:Dataset[Row]:
即 DataFrame = DataSet[Row]:
DataFrame = RDD - 泛型 + Schema + SQL + 优化
DataSet = DataFrame + 泛型
DataSet = RDD + Schema + SQL + 优化
spark/bin/spark-shell
创建 RDD
val lineRDD= sc.textFile("hdfs://node1:8020/person.txt").map(_.split(" ")) //RDD[Array[String]]
case class Person(id:Int, name:String, age:Int)
val personDF = personRDD.toDF //DataFrame
personDF.printSchema
personDF.createOrReplaceTempView("t_person")
spark.sql("select id,name from t_person where id > 3").show
val dataFrame=spark.read.text("hdfs://node1:8020/person.txt")
dataFrame.show //注意:直接读取的文本文件没有完整 schema 信息
dataFrame.printSchema
val jsonDF= spark.read.json("file:///resources/people.json")
接下来就可以使用 DataFrame 的函数操作
jsonDF.show
注意:直接读取 json 文件有 schema 信息,因为 json 文件本身含有 Schema 信 息,SparkSQL 可以自动解析。
val parquetDF=spark.read.parquet("file:///resources/users.parquet")
接下来就可以使用 DataFrame 的函数操作
parquetDF.show
注意:直接读取 parquet 文件有 schema 信息,因为 parquet 文件中保存了列的 信息。
val lineRDD= sc.textFile("hdfs://node1:8020/person.txt").map(_.split(" ")) case class
Person(id:Int, name:String, age:Int)
val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt))
val personDF = personRDD.toDF
personDF.show
//val personDS = personRDD.toDS
//personDS.show
personDF.select(personDF.col("name")).show
personDF.select(personDF("name")).show
personDF.select(col("name")).show
personDF.select("name").show
personDF.select("name", "age").show
personDF.select(personDF.col("name"), personDF.col("age") + 1).show personDF.select(personDF("name"), personDF("age") + 1).show personDF.select(col("name"), col("age") + 1).show
personDF.select("name","age").show
//personDF.select("name", "age"+1).show personDF.select($"name",$"age",$"age"+1).show
personDF.filter(col("age") >= 25).show
personDF.filter($"age" >25).show
personDF.filter(col("age")>30).count()
personDF.filter($"age" >30).count()
personDF.groupBy("age").count().show
personDF.createOrReplaceTempView("t_person")
spark.sql("select * from t_person").show
spark.sql("desc t_person").show
spark.sql("select * from t_person order by age desc limit 2").show
spark.sql("select * from t_person where age > 30 ").show
spark.sql("select name, age + 1 from t_person").show
spark.sql("select name, age from t_person where age > 25").show
spark.sql("select count(age) from t_person where age > 30").show
spark.sql("select age, count(age) from t_person group by age").show
import org.apache.spark.SparkContext
import org.apache.spark.sql.{
DataFrame, Dataset, SparkSession}
object WordCount {
def main(args: Array[String]): Unit = {
//1.创建
SparkSession val spark: SparkSession = SparkSession
.builder()
.master("local[*]")
.appName("Sp arkSQL")
.getOrCreate()
val sc: SparkContext = spark.sparkContext sc.setLogLevel("WARN")
//2.读取文件
val fileDF: DataFrame = spark.read.text("D:\\data\\words.txt")
val fileDS: Dataset[String] = spark.read.textFile("D:\\data\\words.txt")
//fileDF.show()
//fileDS.show()
//3.对每一行按照空格进行切分并压平
//fileDF.flatMap(_.split(" ")) //注意:错误,因为 DF 没有泛型,不知道_是 String import spark.implicits._
val wordDS: Dataset[String] = fileDS.flatMap(_.split(" "))//注意:正确,因为 DS 有泛 型,知道_是 String
//wordDS.show()
/*+-----+ |value|
+-----+ |hello| |
me| |hello| | you|
... */
//4.对上面的数据进行 WordCount wordDS.createOrReplaceTempView("t_word")
val sql = """|select value ,count(value) as count
|from t_word
|group by value
|order by count desc """.stripMargin
spark.sql(sql).show() sc.stop() spark.stop() } }
import org.apache.spark.SparkContext
import org.apache.spark.sql.{
DataFrame, Dataset, SparkSession}
object WordCount2 {
def main(args: Array[String]): Unit = {
//1.创建
SparkSession val spark: SparkSession = SparkSession.builder().master("local[*]").appName("Sp arkSQL").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
//2.读取文件
val fileDF: DataFrame = spark.read.text("D:\\data\\words.txt")
val fileDS: Dataset[String] = spark.read.textFile("D:\\data\\words.txt") /
/fileDF.show()
//fileDS.show()
//3.对每一行按照空格进行切分并压平
//fileDF.flatMap(_.split(" ")) //注意:错误,因为 DF 没有泛型,不知道_是 String import spark.implicits._
val wordDS: Dataset[String] = fileDS.flatMap(_.split(" "))//注意:正确,因为 DS 有泛 型,知道_是 String
//wordDS.show()
/*+-----+ |value|
+-----+ |hello|
| me|
|hello|
| you| ... */
//4.对上面的数据进行 WordCount
wordDS.groupBy("value").count().orderBy($"count".desc).show() sc.stop() spark.stop() } }
spark.read.json("D:\\data\\output\\json").show()
spark.read.csv("D:\\data\\output\\csv").toDF("id","name","age").show()
spark.read.parquet("D:\\data\\output\\parquet").show()
val prop = new Properties()
prop.setProperty("user","root")
prop.setProperty("password","root")
spark.read.jdbc( "jdbc:mysql://localhost:3306/bigdata?characterEncoding=UTF-8","person",prop).show()
personDF.write.json("D:\\data\\output\\json")
personDF.write.csv("D:\\data\\output\\csv")
personDF.write.parquet("D:\\data\\output\\parquet")
val prop = new Properties()
prop.setProperty("user","root")
prop.setProperty("password","root")
personDF.write.mode(SaveMode.Overwrite).jdbc( "jdbc:mysql://localhost:3306/bigdata?characterEncoding=UTF-8","person",prop)