3. Spark SQL

DataFrame 
类似于传统数据库中的二维表格。DataFrame 也是懒执行的,但性能上比 RDD 要高,主要原因:优化的执行计划,即查询计划通过 Spark catalyst optimiser 进行优化。
DataSet
DataSet 是分布式数据集合。DataSet 是 DataFrame API 的一个扩展,是 SparkSQL 最新的数据抽象。
DataSet 是强类型的。比如可以有 DataSet[Car],DataSet[Person]。
DataFrame 是 DataSet 的特列,DataFrame=DataSet[Row] ,所以可以通过 as 方法将DataFrame 转换为 DataSet。Row 是一个类型,跟 Car、Person 这些的类型一样,所有的表结构信息都用 Row 来表示。获取数据时需要指定顺序。
 
SparkSQL 提供两种 SQL 查询起始点:
一个叫 SQLContext,用于 Spark自己提供的 SQL 查询;
一个叫 HiveContext,用于连接 Hive 的查询。
创建 DataFrame有三种方式
1)从 Spark 数据源进行创建
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spark-project</artifactId>
        <groupId>deng.xiake</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spark-sql</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-yarn_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>1.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-hive_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>

    </dependencies>

</project>
pom.xml
log4j.rootCategory=ERROR, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n

# Set the default spark-shell log level to ERROR. When running the spark-shell, the
# log level for this class is used to overwrite the root logger's log level, so that
# the user can have different defaults for the shell and regular Spark apps.
log4j.logger.org.apache.spark.repl.Main=ERROR

# Settings to quiet third party logs that are too verbose
log4j.logger.org.spark_project.jetty=ERROR
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=ERROR
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=ERROR
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR

# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support
log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL
log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR
log4j.properties

读取,存储文件代码

import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession

object SparkSqlDemo {

  def main(args:Array[String]):Unit={
    // 创建环境
    val conf = new SparkConf().setMaster("local").setAppName("deng")

    //
    val spark = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._
    //读取json 文件转化成DataFrame
//    val df = spark.read.json("datas/user.json")
    val df = spark.read.format("json").load("datas/user.json")
    df.show()

    val df2 = spark.read.format("csv").option("sep", ",").option("inferSchema", "true").option("header", "true").load("datas/1.csv")
    df2.show()

    //保存df数据
//    df.write.mode("append").json("/opt/module/data/output")
//    df.write.format("json").save("datas/out")
    df.write.mode("append").format("csv").option("seq",",").save("datas/out")

    spark.stop()

  }
}

RDD ,DataFrame,DataSet 相互转化

import org.apache.spark.sql.{Dataset, SparkSession}
import org.apache.spark.{HashPartitioner, Partitioner, SparkConf, SparkContext}
object SparkSQL {

  def main(args: Array[String]): Unit = {

    //1. 创建sparkSQL的运行环境
    val conf = new SparkConf().setMaster("local").setAppName("sql")
    val spark = SparkSession.builder().config(conf).getOrCreate()
    // 在使用DataFrame时,如果涉及到转换操作,需要引入转换规则
    import spark.implicits._
    val df = spark.read.json("datas/user.json")
    df.show()
    // DataFrame => sql
    df.createOrReplaceTempView("user")
    spark.sql("select avg(age) from user").show()
    // df 操作
    df.select("age").show()
    df.select($"age"+1).show
    //  TODO DataSet 构建DataSet
    // 其实dataFrame是特定泛型的DataSet
    val seq = Seq(1,2,3,4)
    val ds:Dataset[Int] = seq.toDS()
    ds.show()

    // RDD <=> DataFrame
    val rdd = spark.sparkContext.makeRDD(List((1,"zhangshan",30),(2,"lisi",34)))
    val df2 = rdd.toDF("id","name","age")
    df2.show()
    // DataFrame=> RDD
    val rowRDD=df2.rdd
    // DataFrame => DataSet
    val ds2:Dataset[User]=df.as[User]
    // DataSet => DataFrame
    val df3 = ds2.toDF()
    // RDD=> DataSet 有数据有类型
//    val ds3=rdd.map(
//      case(id,name,age)=>{
//      User(id,name,age)
//    }
//    ).toDS()
//    // DataSet =>RDD 把数据和类型去掉
//    val userRDD:RDD[User]=ds3.rdd
    spark.close()
  }

  // 准备样例类
  case class User(id:Int,name:String,age:Int)

}
DataFrame操作
df.printSchema
df.select("username").show()
df.select($"username",$"age" + 1).show()
df.select('username, 'age + 1).show()
df.select('username, 'age + 1 as "newage").show()
df.filter($"age">30).show
df.groupBy("age").count.show
1. RDD 转换为 DataFrame
实际开发中,一般通过样例类将 RDD 转换为 DataFrame
import org.apache.spark.sql.{Dataset, SparkSession}
import org.apache.spark.{HashPartitioner, Partitioner, SparkConf, SparkContext}

object SparkSQL {


  case class User(name: String, age: Int);

  def main(args: Array[String]): Unit = {

    //1. 创建sparkSQL的运行环境
    val conf = new SparkConf().setMaster("local").setAppName("sql")
    val sc = new SparkContext(conf)
    val spark = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._
    val rdd1 = sc.textFile("datas/id.txt")
    /*RDD 转化成DataFrame注意:
    *  样例类需要在main前面
    *  需要引入隐式转换
    * */
    rdd1.toDF("name").show
    sc.makeRDD(List(("zhangsan", 30), ("lisi", 40))).map(t => User(t._1, t._2)).toDF.show
  }

}
 Spark 连接mysql 
在 Idea 中通过 JDBC 对 Mysql 进行操作:
1.导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
</dependency>

2. 读取数据

方式一: 通用的 load 方法读取

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

object SparkSQL_Mysql {
  def main(args: Array[String]): Unit = {
    //1. 创建sparkSQL的运行环境
    val conf = new SparkConf().setMaster("local").setAppName("sql")
    val sc = new SparkContext(conf)
    val spark = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._
    spark.read.format("jdbc").option("url","jdbc:mysql://hadoop103:3306/deng")
      .option("driver", "com.mysql.jdbc.Driver").option("user", "root")
      .option("password", "156")
      .option("dbtable", "user")
      .load().show
  }
}

方式二: 通用的 load 方法读取 参数另一种形式

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

object SparkSQL_Mysql2 {
  def main(args: Array[String]): Unit = {
    //1. 创建sparkSQL的运行环境
    val conf = new SparkConf().setMaster("local").setAppName("sql")
    val sc = new SparkContext(conf)
    val spark = SparkSession.builder().config(conf).getOrCreate()
    spark.read.format("jdbc").options(
      Map(
        "url"->"jdbc:mysql://hadoop103:3306/deng?user=root&password=123456",
        "dbtable"->"user",
        "driver"->"com.mysql.jdbc.Driver"
      )
    ).load().show

  }
}

方式3:使用jdbc 方式

import java.util.Properties

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

object SparkSQL_Mysql2 {
  def main(args: Array[String]): Unit = {
    //1. 创建sparkSQL的运行环境
    val conf = new SparkConf().setMaster("local").setAppName("sql")
    val sc = new SparkContext(conf)
    val spark = SparkSession.builder().config(conf).getOrCreate()
    // 使用jdbc 的方式
    val props= new Properties()
    props.setProperty("user", "root")
    props.setProperty("password", "123456")
    val df = spark.read.jdbc("jdbc:mysql://hadoop103:3306/deng","user",props)
    df.show()

  }
}
Spark 连接外部Hive
需要通过以下几个步骤:
1.Spark 要接管 Hive 需要把 hive-site.xml 拷贝到 conf/目录下
2.把 Mysql 的驱动 copy 到 jars/目录下
3. 如果访问不到 hdfs,则需要把 core-site.xml 和 hdfs-site.xml 拷贝到 conf/目录下
4.重启 spark-shell

 idea 中连接外部hive

1.将 hive-site.xml 文件拷贝到项目的 resources 目录中

2.导入依赖:
<dependency>
 <groupId>org.apache.spark</groupId>
 <artifactId>spark-hive_2.12</artifactId>
 <version>3.0.0</version>
</dependency>
<dependency>
 <groupId>org.apache.hive</groupId>
 <artifactId>hive-exec</artifactId>
 <version>1.2.1</version>
</dependency>
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>5.1.27</version>
</dependency>

3. 代码实现:

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

object SparkSQL_Hive {
  def main(args: Array[String]): Unit = {
    // 遇到权限问题时:
    System.setProperty("HADOOP_USER_NAME", "root")
    //1. 创建sparkSQL的运行环境
    val conf = new SparkConf().setMaster("local").setAppName("sql")
    val sc = new SparkContext(conf)
    val spark = SparkSession.builder().enableHiveSupport().config(conf).getOrCreate()
    import spark.implicits

    spark.sql("show databases").show()


  }
}
注意:在开发工具中创建数据库默认是在本地仓库,通过参数修改数据库仓库的地址:
config("spark.sql.warehouse.dir", "hdfs://linux1:8020/user/hive/warehouse")
如果在执行操作时,出现如下错误: 
注意:在开发工具中创建数据库默认是在本地仓库,通过参数修改数据库仓库的地址:
config("spark.sql.warehouse.dir", "hdfs://linux1:8020/user/hive/warehouse")
如果在执行操作时,出现如下错误: 
此处的 root 改为自己的 hadoop 用户名称
posted @ 2021-07-09 23:48  冰底熊  阅读(114)  评论(0)    收藏  举报