Spark SQL 使用示例

1、Spark SQL

1.1、Spark SQL概述

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

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

官方文檔:http://spark.apache.org/docs/latest/sql-getting-started.html

1.2、特點

1.2.1、集成

無縫地將SQL查詢與Spark程序混合。

Spark SQL允許您使用SQL或熟悉的數據框架API在Spark程序中查詢結構化數據。可用於Java、Scala、Python和R。

results = spark.sql(
  "SELECT * FROM people")
names = results.map(lambda p: p.name)
Apply functions to results of SQL queries.

1.2.2、統一數據訪問

以同樣的方式連接到任何數據源。

DataFrames和SQL提供了訪問各種數據源的通用方法,包括hive、avro、parquet、orc、json和jdbc。您甚至可以跨這些源連接數據。

spark.read.json("s3n://...")
  .registerTempTable("json")
results = spark.sql(
  """SELECT * 
     FROM people
     JOIN json ...""")
Query and join different data sources.

1.2.3、Hive集成

對現有倉庫運行SQL或hiveql查詢。

spark sql支持hiveql語法以及Hive SerDes和UDF,允許您訪問現有的Hive倉庫。

1.2.4、標準連接

通過JDBC或ODBC連接。

服務器模式爲商務智能工具提供行業標準JDBC和ODBC連接。

2、什麼是DataFrames

與RDD類似,DataFrame也是一個分佈式數據容器。然而DataFrame更像傳統數據庫的二維表格,除了數據以外,還記錄數據的結構信息,即schema。同時,與Hive類似,DataFrame也支持嵌套數據類型(struct、array和map)。從API易用性的角度上 看,DataFrame API提供的是一套高層的關係操作,比函數式的RDD API要更加友好,門檻更低。由於與R和Pandas的DataFrame類似,Spark DataFrame很好地繼承了傳統單機數據分析的開發體驗。

2.1、創建DataFrames

進入spark-shell

這裏的Spark session對象(Spark2新增)是對Spark context對象的進一步封裝。也就是說Spark session對象(spark)中的SparkContext就是Spark context對象(sc),從下面輸出信息可以驗證。

1.在本地創建一個person.txt文件,有三列,分別是id、name、age,用空格分隔。

1 張三 18
2 李四 25
3 王五 36
4 趙六 28

2.在spark shell執行下面命令,讀取數據,將每一行的數據使用列分隔符分割

val lineRDD = sc.textFile("file:///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

2.2、DataFrame常用操作

2.2.1、DSL風格語法

//查看DataFrame中的內容
personDF.show

//查看DataFrame部分列中的內容
personDF.select(personDF.col("name")).show
personDF.select(col("name"), col("age")).show
personDF.select("name").show

//打印DataFrame的Schema信息
personDF.printSchema

//查詢所有的name和age,並將age+1
personDF.select(col("id"), col("name"), col("age") + 1).show
personDF.select(personDF("id"), personDF("name"), personDF("age") + 1).show

//過濾age大於等於20的
personDF.filter(col("age") >= 20).show

//按年齡進行分組並統計相同年齡的人數
personDF.groupBy("age").count().show()

2.2.2、SQL風格語法

如果想使用SQL風格的語法,需要將DataFrame註冊成表。

personDF.registerTempTable("t_person")

//查詢年齡最大的前兩名
spark.sql("select * from t_person order by age desc limit 2").show
//顯示錶的Schema信息
spark.sql("desc t_person").show

3、以編程方式執行Spark SQL查詢

前面我們學習瞭如何在Spark Shell中使用SQL完成查詢,現在我們來實現在自定義的程序中編寫Spark SQL查詢程序。首先在maven項目的pom.xml中添加Spark SQL的依賴。

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-sql_2.12</artifactId>
    <version>2.4.3</version>
</dependency>

3.1、通過反射推斷Schema

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SQLContext

object InferringSchema {
  def main(args: Array[String]) {

    //創建SparkConf()並設置App名稱
    val conf = new SparkConf().setAppName("SQL-1")
    //SQLContext要依賴SparkContext
    val sc = new SparkContext(conf)
    //創建SQLContext
    val sqlContext = new SQLContext(sc)

    //從指定的地址創建RDD
    val lineRDD = sc.textFile(args(0)).map(_.split(" "))

    //創建case class
    //將RDD和case class關聯
    val personRDD = lineRDD.map(x => Person(x(0).toInt, x(1), x(2).toInt))
    //導入隱式轉換,如果不到人無法將RDD轉換成DataFrame
    //將RDD轉換成DataFrame
    import sqlContext.implicits._
    val personDF = personRDD.toDF
    //註冊表
    personDF.registerTempTable("t_person")
    //傳入SQL
    val df = sqlContext.sql("select * from t_person order by age desc limit 2")
    //將結果以JSON的方式存儲到指定位置
    df.write.json(args(1))
    //停止Spark Context
    sc.stop()
  }
}
//case class一定要放到外面
case class Person(id: Int, name: String, age: Int)

將程序打成jar包,上傳到spark集羣,提交Spark任務

/usr/local/spark-2.4.3-bin-hadoop2.7/bin/spark-submit \
--class com.learn.spark.sql.InferringSchema \
--master spark://node1.learn.com:7077 \
/root/spark-mvn-1.0-SNAPSHOT.jar \
hdfs://node1.learn.com:9000/person.txt \
hdfs://node1.learn.com:9000/out

查看運行結果

hdfs dfs -cat  hdfs://node1.learn.com:9000/out/part-r-*

3.2、通過StructType直接指定Schema

import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.sql.types._
import org.apache.spark.{SparkContext, SparkConf}

object SpecifyingSchema {
  def main(args: Array[String]) {
    //創建SparkConf()並設置App名稱
    val conf = new SparkConf().setAppName("SQL-2")
    //SQLContext要依賴SparkContext
    val sc = new SparkContext(conf)
    //創建SQLContext
    val sqlContext = new SQLContext(sc)
    //從指定的地址創建RDD
    val personRDD = sc.textFile(args(0)).map(_.split(" "))
    //通過StructType直接指定每個字段的schema
    val schema = StructType(
      List(
        StructField("id", IntegerType, true),
        StructField("name", StringType, true),
        StructField("age", IntegerType, true)
      )
    )
    //將RDD映射到rowRDD
    val rowRDD = personRDD.map(p => Row(p(0).toInt, p(1).trim, p(2).toInt))
    //將schema信息應用到rowRDD上
    val personDataFrame = sqlContext.createDataFrame(rowRDD, schema)
    //註冊表
    personDataFrame.registerTempTable("t_person")
    //執行SQL
    val df = sqlContext.sql("select * from t_person order by age desc limit 4")
    //將結果以JSON的方式存儲到指定位置
    df.write.json(args(1))
    //停止Spark Context
    sc.stop()
  }
}

將程序打成jar包,上傳到spark集羣,提交Spark任務

/usr/local/spark-2.4.3-bin-hadoop2.7/bin/spark-submit \
--class com.learn.spark.sql.InferringSchema \
--master spark://node1.learn.com:7077 \
/root/spark-mvn-1.0-SNAPSHOT.jar \
hdfs://node1.learn.com:9000/person.txt \
hdfs://node1.learn.com:9000/out1

查看結果

hdfs dfs -cat  hdfs://node1.learn.com:9000/out1/part-r-*

4、數據源

Spark SQL可以通過JDBC從關係型數據庫中讀取數據的方式創建DataFrame,通過對DataFrame一系列的計算後,還可以將數據再寫回關係型數據庫中。

4.1、從MySQL中加載數據(Spark Shell方式)

1.啓動Spark Shell,必須指定mysql連接驅動jar包

/usr/local/spark-2.4.3-bin-hadoop2.7/bin/spark-shell \
--master spark://node1.learn.com:7077 \
--jars /usr/local/spark-2.4.3-bin-hadoop2.7/mysql-connector-java-5.1.35-bin.jar \
--driver-class-path /usr/local/spark-2.4.3-bin-hadoop2.7/mysql-connector-java-5.1.35-bin.jar

2.從mysql中加載數據

val jdbcDF = sqlContext.read.format("jdbc").options(Map("url" -> "jdbc:mysql://192.168.10.1:3306/bigdata", "driver" -> "com.mysql.jdbc.Driver", "dbtable" -> "person", "user" -> "root", "password" -> "123456")).load()

3.執行查詢

jdbcDF.show()

4.2、將數據寫入到MySQL中(打jar包方式)

1.編寫Spark SQL程序

import java.util.Properties
import org.apache.spark.sql.{SQLContext, Row}
import org.apache.spark.sql.types.{StringType, IntegerType, StructField, StructType}
import org.apache.spark.{SparkConf, SparkContext}

object JdbcRDD {
  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("MySQL-Demo")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)
    //通過並行化創建RDD
    val personRDD = sc.parallelize(Array("1 tom 5", "2 jerry 3", "3 kitty 6")).map(_.split(" "))
    //通過StructType直接指定每個字段的schema
    val schema = StructType(
      List(
        StructField("id", IntegerType, true),
        StructField("name", StringType, true),
        StructField("age", IntegerType, true)
      )
    )
    //將RDD映射到rowRDD
    val rowRDD = personRDD.map(p => Row(p(0).toInt, p(1).trim, p(2).toInt))
    //將schema信息應用到rowRDD上
    val personDataFrame = sqlContext.createDataFrame(rowRDD, schema)
    //創建Properties存儲數據庫相關屬性
    val prop = new Properties()
    prop.put("user", "root")
    prop.put("password", "123456")
    //將數據追加到數據庫
    personDataFrame.write.mode("append").jdbc("jdbc:mysql://192.168.10.1:3306/bigdata", "bigdata.person", prop)
    //停止SparkContext
    sc.stop()
  }
}

2.用maven將程序打包

3.將Jar包提交到spark集羣

/usr/local/spark-2.4.3-bin-hadoop2.7/bin/spark-submit \
--class com.learn.spark.sql.JdbcRDD \
--master spark://node1.learn.com:7077 \
--jars /usr/local/spark-2.4.3-bin-hadoop2.7/mysql-connector-java-5.1.35-bin.jar \
--driver-class-path /usr/local/spark-2.4.3-bin-hadoop2.7/mysql-connector-java-5.1.35-bin.jar \
/root/spark-mvn-1.0-SNAPSHOT.jar

 

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