Spark编程案例——DataFrame

Spark SQL
1、相应于Hive: SQL —> MapReduce
2、底层依赖RDD: SQL —> RDD

一、Spark SQL基础
1、什么是Spark SQL?
参考官网

2、核心概念:DataFrame(表)= Schema(表结构) + Data(表数据)
                (*)就是表,是Spark SQL对结构化数据的抽象
                (*)DataFrame表现形式就是:RDD
 
             1.6后,新的概念:DataSet(类似DataFrame)
              
3、创建表:DataFrame
    (*)测试数据:员工表emp.csv、部门表dept.csv
        数据 : 7654,MARTIN,SALESMAN,7698,1981/9/28,1250,1400,30
         
    (方式一)使用case class定义表
        (1) 定义case class代表表的结构schema
        case class Emp(empno:Int,ename:String,job:String,mgr:String,hiredate:String,sal:Int,comm:String,deptno:Int)
     
        (2) 导入emp.csv文件(导入数据)
        val lines = sc.textFile("/root/temp/csv/emp.csv").map(_.split(","))
         
        (3) 生成表: DataFrame
        val allEmp = lines.map(x=>Emp(x(0).toInt,x(1),x(2),x(3),x(4),x(5).toInt,x(6),x(7).toInt))
        由allEmp直接生成表
        val empDF = allEmp.toDF

IDEA实现方式:

package dataframe
import org.apache.spark.sql.SparkSession
import org.apache.spark.{SparkConf, SparkContext}
object DataFrameDemo {
  def main(args: Array[String]): Unit = {
    //创建一个Context对象
    val conf = new SparkConf().setAppName("MyJdbcDriverLogCount").setMaster("local");
    val sc = new SparkContext(conf)
    val lines = sc.textFile("D:\\BaiduNetdiskDownload\\emp.csv").map(_.split(","))

    //把每行数据映射到Emp的类上,把数据和Schema关联
    val allEmp = lines.map(x=>Emp(x(0).toInt,x(1),x(2),x(3),x(4),x(5).toInt,x(6),x(7).toInt))

    val spark=  SparkSession.builder()
      .appName("MyJdbcDriverLogCount")
      .config("spark.some.config.option", "some-value")
      .getOrCreate()

    //import implicit DF,DS
    import spark.implicits._

    val empDF = allEmp.toDF()
    empDF.show()

  }
}
case class Emp(empno:Int,ename:String,job:String,mgr:String,hiredate:String,sal:Int,comm:String,deptno:Int)

        (4) 操作: DSL语句
             empDF.show         ---->  select * from emp
             empDF.printSchema  ---->  desc emp
     
    (方式二)使用SparkSession对象
        (1) 什么是SparkSession?
            查看启动spark shell的日志:Spark session available as 'spark'
            从2.0以后,Spark的新的访问接口:提供统一的访问方式
            通过SparkSession可以访问Spark所有的模块
            def createDataFrame(rowRDD: RDD[Row], schema: StructType): DataFrame 
             
        (2) 数据:val lines = sc.textFile("/root/temp/csv/emp.csv").map(_.split(","))
        (3) schema:StructType
                import org.apache.spark.sql._
                import org.apache.spark.sql.types._
         
                val myschema = StructType(List(StructField("empno", DataTypes.IntegerType), StructField("ename", DataTypes.StringType),StructField("job", DataTypes.StringType),StructField("mgr", DataTypes.StringType),StructField("hiredate", DataTypes.StringType),StructField("sal", DataTypes.IntegerType),StructField("comm", DataTypes.StringType),StructField("deptno", DataTypes.IntegerType)))
         
        (4)把读入的每一行数据映射成一个个Row 
            val rowRDD = lines.map(x=>Row(x(0).toInt,x(1),x(2),x(3),x(4),x(5).toInt,x(6),x(7).toInt))
     
        (5) 使用SparkSession.createDataFrame创建表
             val df = spark.createDataFrame(rowRDD,myschema)

IDEA代码方式:

package sparksessiondemo
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{DataFrame, Row, SQLContext, SparkSession}
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types._

object SparkSessionDemo {

  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("MyJdbcDriverLogCount").setMaster("local");
    val sc = new SparkContext(conf)
    val lines = sc.textFile("D:\\BaiduNetdiskDownload\\emp.csv").map(_.split(","))
    val spark = SQLContext.getOrCreate(sc)
    val myschema = StructType(List(StructField("empno", DataTypes.IntegerType), StructField("ename", DataTypes.StringType),
      StructField("job", DataTypes.StringType),StructField("mgr", DataTypes.StringType),StructField("hiredate", DataTypes.StringType),
      StructField("sal", DataTypes.IntegerType),StructField("comm", DataTypes.StringType),StructField("deptno", DataTypes.IntegerType)))
    val rowRDD = lines.map(x=>Row(x(0).toInt,x(1),x(2),x(3),x(4),x(5).toInt,x(6),x(7).toInt))

    val df = spark.createDataFrame(rowRDD,myschema)

    df.show()
  }
}
    (方式三)直接读取一个带格式的文件(json文件) ---> 最简单
            前提:数据文件本身一定具有格式
            Spark Example提供测试数据:$SPARK_HOME/examples/src/main/resources
                [root@bigdata111 resources]# more people.json 
                {"name":"Michael"}
                {"name":"Andy", "age":30}
                {"name":"Justin", "age":19}
             
            (1) cp people.json ~/temp
            (2) 使用SparkSession对象直接读取Json文件
                 val peopleDF = spark.read.json("/root/temp/people.json")
 
 
4、操作表(DataFrame):支持两种语言
    以员工表为例,演示
 
    (1) DSL语句:不常用
         查看所有的员工信息: empDF.show
         查询所有的员工姓名   empDF.select("ename").show
                              empDF.select($"ename").show
          
         查询所有的员工姓名和薪水,并给薪水加100块钱
                empDF.select($"ename",$"sal",$"sal"+100).show
          
         查询工资大于2000的员工
                empDF.filter($"sal" > 2000).show
          
         分组: empDF.groupBy($"deptno").count.show
     
    (2) SQL语句: 前提条件:需要把DataFrame注册成是一个Table或者View
        empDF.createOrReplaceTempView("emp")
         
        使用SparkSession执行从查询
        spark.sql("select * from emp").show
        spark.sql("select * from emp where deptno=10").show
         
        求每个部门的工资总额
        spark.sql("select deptno,sum(sal) from emp group by deptno").show
             
5、视图(虚表): 
    (1)回顾什么是视图:是一个虚表,简化复杂的查询
    (2)Spark SQL有两种视图
            (*)普通视图:empDF.createOrReplaceTempView("emp")
                           只在当前会话中有效
             
            (*)全局视图:empDF.createGlobalTempView("empG")
                           在全局(不同的会话中)有效
                           注意:全局视图是创建在SparkSQL的一个全局“数据库”,前缀:global_temp
                            
            (*)Demo:
                    在当前会话中:
                        spark.sql("select * from emp").show
                        spark.sql("select * from global_temp.empG").show
                     
                    重新开启一个会话
                        spark.newSession.sql("select * from emp").show    ---> 出错
                        spark.newSession.sql("select * from global_temp.empG").show
 
 
6、(了解)DataSet

二、在Spark SQL中使用数据源
1、两个函数:load、save
(*)加载数据:load函数
(1)默认的文件格式:Parquet文件(列式存储文件)
(2)自动生成DataFrame
(3)Demo:cp users.parquet ~/temp/
val userDF = spark.read.load("/root/temp/users.parquet")
对比
val peopleDF = spark.read.json("/root/temp/people.json")
val peopleDF = spark.read.format(“json”).load("/root/temp/people.json")

    (*)保存数据:save函数
         (1)默认的文件格式:Parquet文件(列式存储文件)
         Demo:查询用户的名字和喜欢的颜色,并保存
                 userDF.select($"name",$"favorite_color").write.save("/root/temp/result1")
                  
                 userDF.select($"name",$"favorite_color").write.format("csv").save("/root/temp/result2")
                 userDF.select($"name",$"favorite_color").write.csv("/root/temp/result3")
 
 
2、数据源一:Parquet文件(列式存储文件,是Spark SQL默认的数据源)
    (*)特点:列式存储文件
    (*)把其他格式数据文件转换成Parquet文件
          val empjson = spark.read.json("/root/temp/emp.json")
          empjson.write.parquet("/root/temp/parquet")
           
    (*)Parquet格式支持Schema的合并
            第一个Parquet文件(把RDD转换成Parquet文件)
                val df1 = sc.makeRDD(1 to 5).map(i=>(i,i*2)).toDF("single","double")
                df1.write.parquet("/root/temp/test_table/key=1")
             
            第二个Parquet文件(把RDD转换成Parquet文件)
                val df2 = sc.makeRDD(6 to 10).map(i=>(i,i*3)).toDF("single","triple")
                df2.write.parquet("/root/temp/test_table/key=2")

            合并上面生成Parquet文件
                val df3 = spark.read.option("mergeSchema","true").parquet("/root/temp/test_table/")
         
3、数据源二:json文件
        示例
            val empjson = spark.read.json("/root/temp/emp.json")
            另一种写法
            val empjson1 = spark.read.format("json").load("/root/temp/emp.json")
 
4、数据源三:JDBC直接读取Oracle中的数据,加载成一张表(DataFrame)
    (*)一定注意:以Oracle数据库为例,10g和11g步骤不一样
    (*)Oracle 10g:参考讲义P74
    (*)Oracle 11g为例
            bin/spark-shell --master spark://bigdata111:7077 --jars /root/temp/ojdbc6.jar --driver-class-path /root/temp/ojdbc6.jar
             
        (1) val oracleDF = spark.read.format("jdbc").option("url","jdbc:oracle:thin:@192.168.157.101:1521/orcl.example.com").option("dbtable","scott.emp").option("user","scott").option("password","tiger").load
         
        (2) 定义一个Properties对象保存参数
            def jdbc(url: String, table: String, properties: Properties): DataFrame 
         
            import java.util.Properties
             
            val oracleProp = new Properties()
            oracleProp.setProperty("user","scott")
            oracleProp.setProperty("password","tiger")
             
            val oracleDF1 = spark.read.jdbc("jdbc:oracle:thin:@192.168.157.101:1521/orcl.example.com","scott.emp",oracleProp)
 
5、数据源四:Hive Table
    (*)复习Hive:Hive基于HDFS之上的数据仓库,支持SQL语句----> MapReduce任务
                   Hive 2.x以后,推荐使用Spark作为执行的引擎
                    
    (*)把Hive作为Spark SQL的数据源,完全类似Oracle的数据源
    (*)官方文档 http://spark.apache.org/docs/latest/sql-programming-guide.html#hive-tables
     
    (*)集成Hive和Spark
        (1)安装好Hive
        (2)把Hive的配置文件和Hadoop的配置文件  ----> $SPARK_HOME/conf
                hive-site.xml
                core-site.xml
                hdfs-site.xml
                 
        (3)启动Spark Shell的时候 加入MySQL的驱动
                bin/spark-shell --master spark://bigdata111:7077 --jars /root/training/apache-hive-2.3.0-bin/lib/mysql-connector-java-5.1.43-bin.jar
 
        (4)使用Spark SQL操作Hive
             参考讲义
              
    (*)区别   
            (1)使用Spark SQL操作Hive
            (2)Hive on Spark

三、开发Spark SQL程序
注意:如果不希望在Spark执行中,打印过多的日志,可以使用下面的语句:
Logger.getLogger(“org.apache.spark”).setLevel(Level.ERROR)
Logger.getLogger(“org.eclipse.jetty.server”).setLevel(Level.OFF)

1、创建DataFrame的时候,指定schema
2、使用case class作为表的结构
3、把结果保存到数据库中:使用Spark SQL进行分析,可以把分析的结果保存到关系型数据库中
    Oracle中:
    set linesize 300      ------> 设置行宽,一行上最多显示300个字符
    col stuName for a10   ------> 设置列宽,设置stuName列宽度是10个字符

四、性能优化(了解):缓存、参数

由于之前集成了Hive,从$SPARK_HOME/conf删除下面的文件
                hive-site.xml
                core-site.xml
                hdfs-site.xml   

1、在Spark SQL中如何缓存数据
    举例:读取Oracle数据库
    (*)加载数据DataFrame
    val oracleDF = spark.read.format("jdbc").option("url","jdbc:oracle:thin:@192.168.157.101:1521/orcl.example.com").option("dbtable","scott.emp").option("user","scott").option("password","tiger").load
     
    (*)注册成表:oracleDF.registerTempTable("emp")
         为什么不能使用view?必须是Table   -----> view不能缓存数据
          
    (*)执行查询
         spark.sql("select * from emp").show
          
         缓存表的数据:使用SQLContext对象,可以通过SparkSession获取该对象
         spark.sqlContext.cacheTable("emp")
          
         再执行两次查询
         spark.sql("select * from emp").show  ----> 把结果缓存
         spark.sql("select * from emp").show  ----> 从缓存中读取数据

你可能感兴趣的:(Spark编程案例——DataFrame)