Spark学习(七)---编程操作SparkSQL和关系型数据库读写

这次我们介绍以编程的方式进行sparkSQL的查询和关系型数据库读写,主要有

  • 通过反射推断Schema
  • 通过StructType指定Schema
  • 使用SparkSQL编程操作HiveQL
  • SparkSQL读取数据库文件
  • Spark向关系数据库写入

1. 编程操作SparkSQL

前面我们学习了如何在Spark Shell中使用SQL完成查询,现在我们通过IDEA编写Spark SQL查询程序。
Spark官网提供了两种方法来实现从RDD转换得到DataFrame,

  • 第一种方法是利用反射机制,推导包含某种类型的RDD,通过反射将其转换为指定类型的DataFrame,适用于提前知道RDD的schema。
  • 第二种方法通过编程接口与RDD进行交互获取schema,并动态创建DataFrame,在运行时决定列及其类型。

编程演示

  • 导入pom依赖
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-sql_2.11</artifactId>
    <version>2.0.2</version>
</dependency>

1.1 通过反射推断Schema

Scala支持使用case class类型导入RDD转换为DataFrame,通过case class创建schema,case class的参数名称会被利用反射机制作为列名。这种RDD可以高效的转换为DataFrame并注册为表。

import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession}

/**
  * RDD转化成DataFrame:利用反射机制
  */
//todo:定义一个样例类Person
case class Person(id:Int,name:String,age:Int) 

object CaseClassSchema {

  def main(args: Array[String]): Unit = {
      //todo:1、构建sparkSession 指定appName和master的地址
    val spark: SparkSession = SparkSession.builder()
                              .appName("CaseClassSchema")
                              .master("local[2]").getOrCreate()
      //todo:2、从sparkSession获取sparkContext对象
      val sc: SparkContext = spark.sparkContext
      sc.setLogLevel("WARN")//设置日志输出级别
      //todo:3、加载数据
      val dataRDD: RDD[String] = sc.textFile("D:\\person.txt")
      //todo:4、切分每一行记录
      val lineArrayRDD: RDD[Array[String]] = dataRDD.map(_.split(" "))
      //todo:5、将RDD与Person类关联
      val personRDD: RDD[Person] = lineArrayRDD.map(x=>Person(x(0).toInt,x(1),x(2).toInt))
      //todo:6、创建dataFrame,需要导入隐式转换
      import spark.implicits._
      val personDF: DataFrame = personRDD.toDF()

    //todo-------------------DSL语法操作 start--------------
    //1、显示DataFrame的数据,默认显示20行
    personDF.show()
    //2、显示DataFrame的schema信息
    personDF.printSchema()
    //3、显示DataFrame记录数
    println(personDF.count())
    //4、显示DataFrame的所有字段
    personDF.columns.foreach(println)
    //5、取出DataFrame的第一行记录
    println(personDF.head())
    //6、显示DataFrame中name字段的所有值
    personDF.select("name").show()
    //7、过滤出DataFrame中年龄大于30的记录
    personDF.filter($"age" > 30).show()
    //8、统计DataFrame中年龄大于30的人数
    println(personDF.filter($"age">30).count())
    //9、统计DataFrame中按照年龄进行分组,求每个组的人数
    personDF.groupBy("age").count().show()
    //todo-------------------DSL语法操作 end-------------

    //todo--------------------SQL操作风格 start-----------
    //todo:将DataFrame注册成表
    personDF.createOrReplaceTempView("t_person")
    //todo:传入sql语句,进行操作

    spark.sql("select * from t_person").show()

    spark.sql("select * from t_person where name='zhangsan'").show()

    spark.sql("select * from t_person order by age desc").show()
    //todo--------------------SQL操作风格 end-------------


    sc.stop()
spark.stop()
  }
}

1.2 通过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}

/**
  * RDD转换成DataFrame:通过指定schema构建DataFrame
  */
object SparkSqlSchema {
  def main(args: Array[String]): Unit = {
      //todo:1、创建SparkSession,指定appName和master
      val spark: SparkSession = SparkSession.builder()
                                .appName("SparkSqlSchema")
                                .master("local[2]")
                                .getOrCreate()
      //todo:2、获取sparkContext对象
    val sc: SparkContext = spark.sparkContext
      //todo:3、加载数据
    val dataRDD: RDD[String] = sc.textFile("d:\\person.txt")
      //todo:4、切分每一行
    val dataArrayRDD: RDD[Array[String]] = dataRDD.map(_.split(" "))
      //todo:5、加载数据到Row对象中
    val personRDD: RDD[Row] = dataArrayRDD.map(x=>Row(x(0).toInt,x(1),x(2).toInt))
      //todo:6、创建schema
    val schema:StructType= StructType(Seq(
                                      StructField("id", IntegerType, false),
                                      StructField("name", StringType, false),
                                      StructField("age", IntegerType, false)
                                    ))

     //todo:7、利用personRDD与schema创建DataFrame
    val personDF: DataFrame = spark.createDataFrame(personRDD,schema)

    //todo:8、DSL操作显示DataFrame的数据结果
    personDF.show()

    //todo:9、将DataFrame注册成表
    personDF.createOrReplaceTempView("t_person")
    
    //todo:10、sql语句操作
    spark.sql("select * from t_person").show()

    spark.sql("select count(*) from t_person").show()


    sc.stop()
spark.stop()
  }
}

1.3 编写Spark SQL程序操作HiveContext

HiveContext是对应spark-hive这个项目,与hive有部分耦合, 支持hql,是SqlContext的子类,在Spark2.0之后,HiveContext和SqlContext在SparkSession进行了统一,可以通过操作SparkSession来操作HiveContext和SqlContext。

添加pom依赖

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-hive_2.11</artifactId>
    <version>2.0.2</version>
</dependency>

代码实现
需要在当前项目下创建一个data目录,然后在data目录下创建一个student.txt数据文件

import org.apache.spark.sql.SparkSession
/**
  * todo:Sparksql操作hive的sql
  */
object HiveSupport {
  def main(args: Array[String]): Unit = {
      //todo:1、创建sparkSession
     val spark: SparkSession = SparkSession.builder()
       .appName("HiveSupport")
       .master("local[2]")
       .config("spark.sql.warehouse.dir", "d:\\spark-warehouse")
       .enableHiveSupport() //开启支持hive
       .getOrCreate()
    spark.sparkContext.setLogLevel("WARN")  //设置日志输出级别


    //todo:2、操作sql语句

    spark.sql("CREATE TABLE IF NOT EXISTS person (id int, name string, age int) row format delimited fields terminated by ' '")
    spark.sql("LOAD DATA LOCAL INPATH './data/student.txt' INTO TABLE person")
    spark.sql("select * from person ").show()
    spark.stop()
  }
}

2. SparkSQL进行数据库操作

Spark SQL可以通过JDBC从关系型数据库中读取数据的方式创建DataFrame,通过对DataFrame一系列的计算后,还可以将数据再写回关系型数据库中。

2.1 通过IDEA编写SparkSql代码

import java.util.Properties

import org.apache.spark.sql.{DataFrame, SparkSession}


object DataFromMySql{

  def main(args: Array[String]): Unit = {
    //1、创建sparkSession
    val spark: SparkSession = SparkSession.builder()
      .appName("DataFromMysql")
      .master("local[2]")
      .getOrCreate()

    //2、通过sparkSession获取mysql表中的数据
    //准备配置属性
    val properties = new Properties()
    //设置用户名和密码
    properties.setProperty("user","root")
    properties.setProperty("password","123")

    val dataFrame: DataFrame = spark.read.jdbc("jdbc:mysql://localhost:3306/user","iplocation",properties)

    //打印dataFrame schema信息
    dataFrame.printSchema()

    //打印dataFrame中的数据
    dataFrame.show()

    //关闭
    spark.stop()
  }
}

2.2 通过spark-shell运行

  • 启动spark-shell(必须指定mysql的连接驱动包)
spark-shell \
--master spark://nodel01: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
  • 执行查询
//加载数据
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" -> "123")).load()

//展示数据
mysqlDF.show

2.3 将数据存储到MySQL数据库中

  • 编写代码
import java.util.Properties

import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession}

case class Student(id:Int,name:String,age:Int)
class DataToMysql {

}
object DataToMysql{

  def main(args: Array[String]): Unit = {
    //1、创建sparkSession,集群模式去掉master
    val spark: SparkSession = SparkSession.builder().appName("DataToMysql").master("local[2]").getOrCreate()
    //2、通过sparkSession获取sparkContext
    val sc: SparkContext = spark.sparkContext
    sc.setLogLevel("WARN")
    //3、通过sparkContext加载数据文件,修改文件位置为“args[0]”外部设置参数
    val data: RDD[String] = sc.textFile("d:\\temp\\a.txt")
    //4、切分每一行
    val linesArrayRDD: RDD[Array[String]] = data.map(_.split(" "))
    //5、linesArrayRDD与样例类关联
    val personRDD: RDD[Student] = linesArrayRDD.map(x=>Student(x(0).toInt,x(1),x(2).toInt))
    //6、将personRDD转化为dataframe

    //手动导入隐式转换
    import spark.implicits._
    val personDF: DataFrame = personRDD.toDF

    //personDF注册成一张表
    personDF.createTempView("t_student")
    //打印dataframe中的结果数据
    personDF.show()

    //操作表
    val resultDF: DataFrame = spark.sql("select * from t_student order by age desc")

    //将resultDF写入到mysql表中
    val properties = new Properties()
    properties.setProperty("user","root")
    properties.setProperty("password","123")

    //mode需要对应4个参数
    //overwrite:覆盖(它会帮你创建一个表,然后进行覆盖)
    //append:追加(它会帮你创建一个表,然后把数据追加到表里面)
    //ignore:忽略(它表示只要当前表存在,它就不会进行任何操作)
    //ErrorIfExists:只要表存在就报错(默认选项)
 //修改"student" 为"args[1]"   resultDF.write.mode("overwrite").jdbc("jdbc:mysql://localhost:3306/user","student",properties)

    //关闭
    sc.stop()
    spark.stop()

  }
}

  • 用maven将程序打包,打包时候修改设置spark的地方,去掉master(“local[2]”)
    对文件输入位置和表信息修改,修改内容在注释中
  • 将Jar包提交到spark集群
spark-submit \
--class itcast.sql.SparkSqlToMysql \
--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 $hive/lib/mysql-connector-java-5.1.35.jar \
/root/original-spark-2.0.2.jar  /person.txt
  • 查看mysql中表的数据

本次介绍结束

猜你喜欢

转载自blog.csdn.net/weixin_42229056/article/details/83240178
今日推荐