【spark】spark SQL概述 RDD、DataFrame及DataSet開發 相互轉換 以及sparksql和mysql數據轉換

一、sparksql概述

Spark SQL是Spark用來處理結構化數據的一個模塊,它提供了一個編程抽象叫做DataFrame並且作爲分佈式SQL查詢引擎的作用。

相比於Spark RDD API,Spark SQL包含了對結構化數據和在其上運算的更多信息,Spark SQL使用這些信息進行了額外的優化,使對結構化數據的操作更加高效和方便

Hive,是將Hive SQL轉換成MapReduce然後提交到集羣中去執行,大大簡化了編寫MapReduce程序的複雜性,由於MapReduce這種計算模型執行效率比較慢,所以Spark SQL應運而生,它是將Spark SQL轉換成RDD,然後提交到集羣中去運行,執行效率非常快!

二、sparksql四大特性

  • 易整合
    • 將sql查詢與spark程序無縫混合,可以使用java、scala、python、R等語言的API操作
  • 統一的數據源訪問
    • sparksql以相同方式訪問任意數據源
    • SparkSession.read.文件格式方法(對應文件格式的路徑)
  • 兼容hive
    • 支持hivesql的語法
  • 標準的數據連接
    • 可以使用行業的jdbc和odbc來連接數據庫

三、DataFrame簡介

在Spark中,DataFrame是一種以RDD爲基礎的分佈式數據集,類似於傳統數據庫的二維表格,DataFrame帶有Schema元信息,即DataFrame所表示的二維表數據集的每一列都帶有名稱和類型,但底層做了更多的優化.

DataFrame與RDD的區別
  1. rdd裏面存放的是java對象,dataframe來說它裏面存放的是Row對象,Row也就是說把每一行數據封裝在一個Row對象

  2. dataframe中除了數據之外,還包括了數據結構信息,這個結構信息,我們叫做schema(比如當前它有哪些列名稱和列的類型)

  3. DataFrame還引入了off-heap,意味着JVM堆以外的內存, 這些內存直接受操作系統管理(而不是JVM)。

在這裏插入圖片描述

DataFrame與RDD的優缺點

1、rdd優缺點

  • 優點
    1、編譯時類型安全
    2、面向對象編程的風格
  • 缺點
    1、序列化和反序列化性能開銷很多
    2、GC性能開銷:頻繁的創建對象和銷燬,會帶來大量的GC

2、dataFrame的優缺點
dataFrame它引入schema和off-heap(使用不在jvm堆以內的內存,直接使用操作系統中的內存)

  • 優點
    1. 引入了schema解決了rdd的這個缺點(序列化和反序列化性能開銷很多)
    2. 引入了off-heap解決了rdd的這個缺點(GC性能開銷很大)
  • 缺點
    • 丟失了RDD的優點
    • 不在是編譯時類型安全
    • 也不是面向對象編程風格
讀取數據源創建DataFrame

讀取文本文件創建DataFrame
在這裏插入圖片描述

(1)在本地創建一個文件,有三列,分別是id、name、age,用空格分隔,然後上傳到hdfs上。person.txt內容爲:

1 zhangsan 20
2 lisi 29
3 wangwu 25
4 zhaoliu 30
5 tianqi 35
6 kobe 40

上傳數據文件到HDFS上:

hdfs dfs -put person.txt  /

(2)在spark shell執行下面命令,讀取數據,將每一行的數據使用列分隔符分割
先執行 spark-shell --master local[2]

val lineRDD= sc.textFile("/person.txt").map(_.split(" "))

在這裏插入圖片描述

(3)定義case class(相當於表的schema)

case class Person(id:Int, name:String, age:Int)

在這裏插入圖片描述

(4)將RDD和case class關聯

val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt))

在這裏插入圖片描述

(5)將RDD轉換成DataFrame

val personDF = personRDD.toDF

在這裏插入圖片描述

(6)對DataFrame進行處理

personDF.show

在這裏插入圖片描述

personDF.printSchema

在這裏插入圖片描述

(7)、通過SparkSession構建DataFrame
使用spark-shell中已經初始化好的SparkSession對象spark生成DataFrame

val dataFrame=spark.read.text("/person.txt")

在這裏插入圖片描述

讀取json文件創建DataFrame

(1)數據文件
使用spark安裝包下的
/opt/bigdata/spark/examples/src/main/resources/people.json文件

(2)在spark shell執行下面命令,讀取數據

val jsonDF= spark.read.json("file:///opt/bigdata/spark/examples/src/main/resources/people.json")

在這裏插入圖片描述

(3)接下來就可以使用DataFrame的函數操作

在這裏插入圖片描述
在這裏插入圖片描述

四、DataFrame常用操作

DSL風格語法

DataFrame提供了一個領域特定語言(DSL)來操作結構化數據。

//創建rdd
val rdd1=sc.textFile("/person.txt").map(_.split(" "))
//定義樣例類
case class Person(id:Int,name:String,age:Int)
//rdd於樣例類關聯
val rdd2=rdd1.map(x => Person(x(0).toInt,x(1),x(2).toInt))
//rdd轉換成dataFrame
val personDF=rdd2.toDF 
//打印schema
personDF.printSchema
//查詢數據
personDF.show
//查詢name字段
personDF.select("name").show
personDF.select($"name").show
personDF.select(col("name").show
//實現age字段結果加1
personDF.select($"name",$"age",$"age"+1).show
//查詢age大於30的用戶信息
personDF.filter($"age" >30).show
//查詢age大於30的用戶人數
personDF.filter($"age" >30).count
//按照age進行分組統計不同的age出現的人的次數
presonDF.groupBy("age").count.show
SQL風格語法

可以把DataFrame看成是一張關係型數據表

  1. 需要把dataFrame註冊成一張表
 presonDF.registerTempTable("t_person")
  1. 通過SparkSession調用sql方法,傳入對應sql語句
    spark.sql(sql語句)
spark.sql("select * from t_person").show
spark.sql("select * from t_person where id=1").show
spark.sql("select * from t_person order by age desc").show

六、DataSet

DataSet是分佈式的數據集合,Dataset提供了強類型支持,也是在RDD的每行數據加了類型約束。DataSet是在Spark1.6中添加的新的接口。它集中了RDD的優點(強類型和可以用強大lambda函數)以及使用了Spark SQL優化的執行引擎.

DataFrame、DataSet、RDD的區別

假設RDD中的兩行數據長這樣:
在這裏插入圖片描述

那麼DataFrame中的數據長這樣:
在這裏插入圖片描述

那麼Dataset中的數據長這樣:
在這裏插入圖片描述

或者長這樣(每行數據是個Object):
在這裏插入圖片描述

DataSet包含了DataFrame的功能,Spark2.0中兩者統一,DataFrame表示爲DataSet[Row],即DataSet的子集。

  1. DataSet可以在編譯時檢查類型
  2. 並且是面向對象的編程接口
DataFrame與DataSet互相轉換
  1. DataFrame轉換成DataSet
    ○ df.as[強類型]
  2. DataSet轉換成DataFrame
    ○ ds.toDF

創建DataSet

(1)通過spark.createDataset創建
在這裏插入圖片描述
在這裏插入圖片描述

(2)通toDS方法生成DataSet
在這裏插入圖片描述

(3)通過DataFrame轉化生成
使用**as[類型]**轉換爲DataSet
在這裏插入圖片描述

七、編程實現RDD轉換成DataFrame

利用反射機制

導入依賴
 <dependency>
       <groupId>org.apache.spark</groupId>
       <artifactId>spark-sql_2.11</artifactId>
       <version>2.0.2</version>
 </dependency>
代碼開發
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{Column, DataFrame, SparkSession}
//todo:需求:將rdd轉換成dataFrame(利用反射機制)
case class Person(id:Int,name:String,age:Int)
object CaseClassSchema {
  def main(args: Array[String]): Unit = {
     //1、創建SparkSession對象
        val spark: SparkSession = SparkSession.builder().appName("CaseClassSchema").master("local[2]").getOrCreate()
//2、獲取SparkContext
        val sc: SparkContext = spark.sparkContext
        sc.setLogLevel("WARN")
     //3、讀取文件
        val data: RDD[Array[String]] = sc.textFile("E:\\person.txt").map(_.split(" "))
     //4、將rdd與樣例類關聯
        val personRDD: RDD[Person] = data.map(x => Person(x(0).toInt,x(1),x(2).toInt))
     //5、將rdd轉換成dataFrame
       //手動導入隱式轉換
      import spark.implicits._
      val personDF: DataFrame = personRDD.toDF
//------------------DSL風格語法------------start
    //打印schema元信息
    personDF.printSchema()
    //顯示數據,默認展示20條數據
    personDF.show()
    //展示第一條數據
    println(personDF.first())
//查詢name字段對應的結果數據
      personDF.select("name").show()
      personDF.select($"name").show()
      personDF.select(new Column("name")).show()
//把age字段對應的結果加1
      personDF.select($"name",$"age",$"age"+1).show()
//過濾年齡大於30的人的信息
      personDF.filter($"age" >30).show()
//過濾年齡大於30的人數
     println(personDF.filter($"age" >30).count())
//按照age分組統計不同年齡出現的次數
    personDF.groupBy("age").count().show()
//------------------DSL風格語法------------end
//------------------SQL風格語法-------------start
    personDF.createTempView("t_person")
    spark.sql("select * from t_person").show()
    spark.sql("select * from t_person where id =1").show()
    spark.sql("select * from t_person order by age desc").show()
    //------------------SQL風格語法-------------end
//關閉sparkSession
      spark.stop()
  }
}
通過StructType直接指定Schema

當case class不能提前定義好時,可以通過以下三步創建DataFrame

  1. 將RDD轉爲包含Row對象的RDD
  2. 基於StructType類型創建schema,與第一步創建的RDD相匹配
  3. 通過sparkSession的createDataFrame方法對第一步的RDD應用schema創建DataFrame
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
//todo:需求:將rdd轉換成dataFrame(通過StructType指定schema)
object SparkSqlSchema {
  def main(args: Array[String]): Unit = {
      //1、創建SparkSession
      val spark: SparkSession = SparkSession.builder().appName("SparkSqlSchema").master("local[2]").getOrCreate()
//2、創建SparkContext
        val sc: SparkContext = spark.sparkContext
        sc.setLogLevel("WARN")
//3、讀取文件數據
        val data: RDD[Array[String]] = sc.textFile("e:\\person.txt").map(_.split(" "))
//4、把RDD[Array[String]] 轉換成RDD[Row]
         val rowRDD: RDD[Row] = data.map(x => Row(x(0).toInt,x(1),x(2).toInt))
//5、指定dataFrame的schema
     val schema = StructType(
                    StructField("id", IntegerType, true) ::
                    StructField("name", StringType, false) ::
                    StructField("age", IntegerType, false) :: Nil)
val personDF: DataFrame = spark.createDataFrame(rowRDD,schema)
    //打印schema
    personDF.printSchema()
    //打印結果數據
    personDF.show()
//dataFrame註冊成一張表
    personDF.createTempView("t_person")
spark.sql("select * from t_person").show()
//關閉
    spark.stop()
  }
}
編寫Spark SQL程序操作HiveContext

HiveContext是對應spark-hive這個項目,與hive有部分耦合, 支持hql,是SqlContext的子類,在Spark2.0之後,HiveContext和SqlContext在SparkSession進行了統一,可以通過操作SparkSession來操作HiveContext和SqlContext。

導入依賴
 <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-hive_2.11</artifactId>
      <version>2.0.2</version>
 </dependency>
代碼開發
import org.apache.spark.sql.SparkSession
//todo:需求:利用sparkSQL操作hivesql
object SupportHive {
  def main(args: Array[String]): Unit = {
      //1、創建SparkSession
      val spark: SparkSession = SparkSession.builder()
                                            .appName("SupportHive")
                                            .master("local[2]")
                                            .enableHiveSupport() //開啓sparksql支持hivesql
                                            .getOrCreate()
     //2、可以操作hivesql
        //2.1創建hive表
        spark.sql("create table student(id int,name string,age int) row format delimited fields terminated by ',' ")
        //2.2 加載數據到hive表中
        spark.sql("load data local inpath './data/student.txt' into table student")
        //2.3 查詢表數據
        spark.sql("select * from student").show()
//關閉sparkSession
    spark.stop()
  }
}

八、SparkSql從MySQL中加載數據

通過IDEA編寫SparkSql代碼

代碼開發
import java.util.Properties
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
  * todo:Sparksql從mysql中加載數據
  */
object DataFromMysql {
  def main(args: Array[String]): Unit = {
      //todo:1、創建sparkSession對象
      val spark: SparkSession = SparkSession.builder()
        .appName("DataFromMysql")
        .master("local[2]")
        .getOrCreate()
    //todo:2、創建Properties對象,設置連接mysql的用戶名和密碼
    val properties: Properties =new Properties()
    properties.setProperty("user","root")
    properties.setProperty("password","123456")
    //todo:3、讀取mysql中的數據
    val mysqlDF: DataFrame = spark.read.jdbc("jdbc:mysql://192.168.200.100:3306/spark","iplocation",properties)
    //todo:4、顯示mysql中表的數據
    mysqlDF.show()
    spark.stop()
  }
}
運行結果

在這裏插入圖片描述

通過spark-shell運行

(1)、啓動spark-shell(必須指定mysql的連接驅動包)

spark-shell \
--master spark://hdp-node-01:7077 \
--executor-memory 1g \
--total-executor-cores  2 \
--jars /opt/bigdata/hive/lib/mysql-connector-java-5.1.35.jar \
--driver-class-path /opt/bigdata/hive/lib/mysql-connector-java-5.1.35.jar

(2)、從mysql中加載數據

val mysqlDF = spark.read.format("jdbc").options(Map("url" -> "jdbc:mysql://192.168.200.150:3306/spark", "driver" -> "com.mysql.jdbc.Driver", "dbtable" -> "iplocation", "user" -> "root", "password" -> "123456")).load()

(3)、執行查詢

在這裏插入圖片描述

九、SparkSql將數據寫入MySql中

通過IDEA編寫SparkSql代碼

(1)編寫代碼

import java.util.Properties
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession}
//todo:需求:通過sparksql把結果數據寫入mysql表中
case class People(id:Int,name:String,age:Int)
object Data2Mysql {
  def main(args: Array[String]): Unit = {
    //1、創建SparkSession
      val spark: SparkSession = SparkSession.builder().appName("Data2Mysql").master("local[2]").getOrCreate()
//2、創建SparkContext
        val sc: SparkContext = spark.sparkContext
        sc.setLogLevel("WARN")
    //3、讀取數據文件
      val data: RDD[Array[String]] = sc.textFile("e:\\person.txt").map(_.split(" "))
//4、rdd與樣例類關聯
      val peopleRDD: RDD[People] = data.map(x=>People(x(0).toInt,x(1),x(2).toInt))
//5、將rdd轉換成dataFrame
      import spark.implicits._
       val peopleDF: DataFrame = peopleRDD.toDF
//打印schema
      peopleDF.printSchema()
      peopleDF.show()
//註冊成一張表
      peopleDF.createTempView("people")
      val result: DataFrame = spark.sql("select * from people where age >24")
//把結果數據寫入到mysql表中
      //定義url
      val url="jdbc:mysql://192.168.200.100:3306/spark"
      //定義table表名
      val table="people"
      //定義相關屬性
      val properties = new Properties()
      properties.setProperty("user","root")
      properties.setProperty("password","123456")
//mode方法:指定數據插入模式
           //overwrite:表示覆蓋(表不存在,事先幫你創建)
           //append: 追加(表不存在,事先幫你創建)
           //ignore:忽略 (只要表存在,就不進行任何操作)
           //error: 默認選項,如果表存在就報錯
      result.write.mode("ignore").jdbc(url,table,properties)
//關閉
      spark.stop()
  }
}
通過spark-shell運行

(1)編寫代碼
只需把上面代碼中的文件來源及表名當做參數傳入即可

import java.util.Properties
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession}
//todo:需求:通過sparksql把結果數據寫入mysql表中
case class People(id:Int,name:String,age:Int)
object Data2Mysql {
  def main(args: Array[String]): Unit = {
    //1、創建SparkSession
      val spark: SparkSession = SparkSession.builder().appName("Data2Mysql").getOrCreate()
//2、創建SparkContext
        val sc: SparkContext = spark.sparkContext
        sc.setLogLevel("WARN")
    //3、讀取數據文件
      val data: RDD[Array[String]] = sc.textFile(args(0)).map(_.split(" "))
//4、rdd與樣例類關聯
      val peopleRDD: RDD[People] = data.map(x=>People(x(0).toInt,x(1),x(2).toInt))
//5、將rdd轉換成dataFrame
      import spark.implicits._
       val peopleDF: DataFrame = peopleRDD.toDF
//打印schema
      peopleDF.printSchema()
      peopleDF.show()
//註冊成一張表
      peopleDF.createTempView("people")
      val result: DataFrame = spark.sql("select * from people where age >24")
//把結果數據寫入到mysql表中
      //定義url
      val url="jdbc:mysql://192.168.200.100:3306/spark"
      //定義table表名
      val table=args(1)
      //定義相關屬性
      val properties = new Properties()
      properties.setProperty("user","root")
      properties.setProperty("password","123456")
//mode方法:指定數據插入模式
           //overwrite:表示覆蓋(表不存在,事先幫你創建)
           //append: 追加(表不存在,事先幫你創建)
           //ignore:忽略 (只要表存在,就不進行任何操作)
           //error: 默認選項,如果表存在就報錯
      result.write.mode("append").jdbc(url,table,properties)
//關閉
      spark.stop()
  }
}

(2)提交任務腳本

spark-submit --master spark://node1:7077 --class cn.itcast.sql.Data2Mysql --executor-memory 1g --total-executor-cores 2 --jars /export/servers/hive/lib/mysql-connector-java-5.1.35.jar --driver-class-path /export/servers/hive/lib/mysql-connector-java-5.1.35.jar original-spark_class07-2.0.jar /person.txt person2018
 

注意:應保證數據庫外部訪問權限

 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY '123' WITH GRANT OPTION;
flush privileges;

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章