Spark sql介绍

是Spark用来处理结构化数据的一个模块,它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用。(在Spark中使用sql查询)

  • DataFrame可处理结构化数据,所以Spark sql中先将数据集转化为RDD再将RDD转化为DataFrames对象,之后使用sql查询数据。

Spark sql应用

一、 在Spark-shell中:SparkContext和SQLContext是创建好的 所以不需要创建

  • 将数据集转换RDD(分布式数据集),再将RDD转换为DataFrames对象(保存数据信息和数据结构信息[相当于数据库中的表])
val seq= Seq(("1","xiaoming",15),("2","xiaohong",20),("3","xiaobi",10))----数据集存为List
 val rdd1 = sc.parallelize(seq)----转换为RDD,实现并行化
 rdd1.toDF----转换为DataFrames对象(空参下数据的列名以数字1依次递增)----查询时直接.show即可
 val df = rdd1.toDF("id","name","age")----传入参数,参数依次对应数据集中的数据设为列名,因为指定了对象名故查询时使用df.show
  • 转换为DataFrames之后就可以执行查询了:
    查询时有两种查询风格:DSL风格查询(特定邻域下的语言)和SQL风格查询(在spark交互中使用SQ语句查询)

1.DSL风格查询:

查询所有:df.show
以列名查询:df.select("列名").show
条件过滤:df.select().filter().show----括号中是字符类型
查看DataFrames的数据结构:df.printSchema

2.SQL风格查询:先转换为临时表再查询

转换----将DataFrame注册成表(临时表),表会被存储:df.registerTempTable("临时表名")
查询----spark.sqlContext.sql("SQL语句").show

二、在IDEA中

  • 先导依赖:注意版本
<dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.11</artifactId>
            <version>${spark.version}</version>
        </dependency>
  1. 方式一:通过toDF将RDD转化为dataframes,相当于反射,这里若要使用的话,需要导入包:new SQLContext(sc)下的。
  • 直接调用toDF的时候,使用的就是默认列名:数字从1开始逐渐递增;在指定列名时列名要和数据一一对应
  • 可以在调用toDF方法的时候指定类的名称(指定名称多余数据会报错)
//方式一:registerTempTable即将淘汰,横线划住
object SparkSqlTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[5]").setAppName("SparkSql")
    val sc = new SparkContext(conf)

    val lines = sc.textFile("D:\\data\\person.txt")
    val value: RDD[Array[String]] = lines.map(line => {line.split(" ")})
//  将获取的数据关联到样例类中
    val person: RDD[Person] = value.map(line => {Person(line(0).toInt,line(1).toString,line(2).toInt)})
    person.foreach(x=>{println(x)})
//  使用sql语句查询
    val sqlc = new SQLContext(sc)
    import sqlc.implicits._
    val frame: DataFrame = person.toDF()
    frame.show()
    frame.registerTempTable("t_person")
    val sqls = "select * from t_person"
    val frame1 = sqlc.sql(sqls)
    frame1.show()

    frame1.write.mode("append").save("D:\\data\\person\\result.txt")
    sc.stop()
  }
//样例类
  case class Person(id:Int,name:String,age:Int)
}

注意点

  1. 样例类:相当于new出的构造方法,在object中使用时不再需要new了。
    使用代码编程时数据是存储到样例类中,样例类中的构造方法中的参数就是对应的列名,所以通过toDF可以直接获取对应的属性名作为列名使用;同时也可以自定义列名
  2. 写出:
    model的参数:Overwrite --复写;Append ----- 追加
    save的参数:写出的地址
  • 方式二:通过createDataFrame将RDD转化为dataframes
  • 先创建StructType对象
  • 再将数据获取
  • 最后传入参数转换为DataFrames
//方式二:StructType封装数据结构
object SqlTest{
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[5]").setAppName("SqlTest")
    val sc = new SparkContext(conf)
    val sqls = new SQLContext(sc)
    val lines: RDD[String] = sc.textFile("D:\\data\\person.txt")
    val word: RDD[Array[String]] = lines.map(_.split(" "))
//  封装数据结构
    val structType: StructType = StructType {
      List(
//      参数:列名 数据类型 是否可以为空
        StructField("id", IntegerType, false),
        StructField("name", StringType, true),
        StructField("age", IntegerType, false)
      )
    }
    val row: RDD[Row] = word.map(arr => Row(arr(0).toInt,arr(1),arr(2).toInt))
    val frame: DataFrame = sqls.createDataFrame(row,structType)
    frame.show()
    sc.stop()
  }
}

注意点

  1. StructType对象:用于分装数据结构(StructField用于存放字段即列名)
  2. 其中定义的列名数据大于存放数据时,所对应列的值应该是null;定义的列名数据是不能小于存放数据的,不然会抛出异常
  • 方式三:将结果放入数据库
//方式三:将结果放入到数据库
object WriteSqlTest{
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[5]").setAppName("WriteSqlTest")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)

    val line: RDD[Array[String]] = sc.textFile("D:\\data\\person.txt").map(_.split(" "))
    val data: RDD[Row] = line.map(x => Row(x(0).toInt,x(1),x(2).toInt))

    val structType: StructType = StructType {
      Array(
        StructField("id", IntegerType, false),
        StructField("name", StringType, true),
        StructField("age", IntegerType, false
        )
      )
    }

    val frame = sqlContext.createDataFrame(data,structType)

    val properties: Properties = new Properties()
    properties.put("user","root")
    properties.put("password","1234")
    properties.put("driver","com.mysql.cj.jdbc.Driver")

    val jdbcurl = "jdbc:mysql://localhost/spark?characterEncoding=utf-8&serverTimezone=UTC"

    val table = "person"

    frame.write.mode("append").jdbc(jdbcurl,table,properties)
    println("成功")
    sc.stop()
  }
}

注意点

  1. 要导入mysql连接的依赖
<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
  • jdbc的参数:(url,table,properties)写出到MySQL
    jdbcurl----------连接的数据库
    table------------表名
    Properties-------用于连接数据库,配置用户,密码,驱动程序

三、HIVE-on-Spark:Hive的底层是通过MapReduce进行计算,当spark使用hive时要将其改变为sparkCore来执行(由于MapReduce中间计算均需要写入磁盘,而Spark是放在内存中,所以总体来讲Spark比MapReduce快很多。)

  • 详解Hive on Spark
  • 该项目的目的是把Spark作为Hive的一个计算引擎,将Hive的查询作为Spark的任务提交到Spark集群上进行计算。通过该项目,可以提高Hive查询的性能,同时为已经部署了Hive或者Spark的用户提供了更加灵活的选择,从而进一步提高Hive和Spark的普及率。
  • 在该项目下,使用Hive的标准在Spark集群上运行。
  • 真正要计算的数据是保存在HDFS中的,mysql这个元数据库保存的是hive表的描述信息。
  • 应用
  • 前期准备:
    1.将hive安装路径下的hive-site.xml拷贝到spark的配置conf配置文件目录
    2.不高可用机制下:将Hadoop安装目录中的core-site.xml拷贝到spark的配置conf文件目录下
    高可用机制下:将hadoop安装路径下的core-site,xml和hdfs-site.xml拷到spark的conf目录下
    3.将数据库连接的jar包mysql-connector-java-5.1.39.jar拷入到Linux下
    4.准备数据源(/root/person.txt)
  • 启动集群在bin下启动spark-sql交互窗口,进入到spark-sql交互窗口后就可以使用hive标准查询数据了:
./spark-sql --master spark://hdp-0:7077(Spark的URL) --executor-memory 512m --total-executor-cores 2 --jars  mysql连接的jar包(全路径) --driver-class-path jar包所在位置

建表:create table person(id int,name string,age int) row format delimited fields terminated by ' ';
加载数据:load data local inpath '/root/person.txt' into table person;
处理数据:sql语句