Spark SQL概述

Hive:将SQL语句转换成MapReduce程序执行,Hive SQL查询的MapReduce作业转化过程:

  1. 输入
  2. 将SQL转换成抽象语法树
  3. 将抽象语法树转换成查询块
  4. 将查询块转换成逻辑查询计划
  5. 重写逻辑查询计划
  6. 将逻辑查询计划转成物理计划
  7. 选择最佳的优化查询策略
  8. 输出

Shark:即Hive on Spark,为了实现与Hive兼容,Shark在HiveQL方面重用了Hive中HiveQL的解析、逻辑执行计划翻译、执行计划优化等逻辑,可以近似认为仅将物理执行计划从MapReduce作业替换成了Spark作业,通过Hive的HiveQL解析,把HiveQL翻译成Spark上的RDD操作

Shark的有缺点:

优点:Shark对比Hive性能提升10~100倍

缺点:

  1. 执行计划的优化完全依赖Hive,不方便添加新的优化策略
  2. 因为spark是线程级并行,而MapReduce是进程级并行,因此,Spark在兼容Hive的实现上存在线程安全问题,导致Shark不得不使用另外一套独立维护的打了补丁的Hive源码分支

Shark停止服务后:

Spark SQL: 作为Spark生态的一员继续发展,而不再受限于Hive,指示兼容Hive

Hive on Spark:是一个Hive的发展计划,该计划将Spark作为Hive的底层引擎之一,也就是说Hive将不再受限于一个引擎,可以采用MapReduce、Tez、Spark等引擎

Spark SQL在Hive兼容层面仅依赖HiveQL解析、Hive元数据,也就是说,从HQL被解析成抽象语法树(AST)起,就全部由Spark SQL接管了。Spark SQL执行计划生成和优化Catalyst(函数式关系查询优化框架)负责

Spark SQL的设计:Spark SQL增加了DataFrame(即带有Schema信息的RDD),使用户可以在Spark SQL中执行SQL语句,数据既可以是Hive,HDFS,Cassandra等外部数据源,还可以是json格式数据

DataFrame与RDD的区别

  1. DataFrame的推出,让Spark具备了处理大规模结构化数据的能力,不仅比原有的RDD转化方式更加简单易用,而且获得了更高的计算能力,Spark能够轻松实现从MySQL到DataFrame的转化,并且支持SQL查询
  2. RDD是分布式的Java对象的集合,但是对象内部结构对于RDD而言却是不可知的,DataFrame是一种以RDD为基础的分布式数据集,提供了详细的结构信息

DataFrame的创建

  1. SparkSession的由来:从Spark2.0以上版本开始,Spark使用全新的SparkSession接口代替Spark1.6中的SQLContext及HiveContext接口来实现其对数据加载、转换、处理等功能。SparkSession实现了SQLContext及HiveContext所有功能
  2. SparkSesson的功能:SparkSession支持从不同的数据源加载数据,并把数据转换成DataFrame,并且支持把DataFrame转换成SQLContext自身中的表,然后使用SQL语句来操作数据。SparkSession亦提供和HiveQL以及其他依赖于Hive的功能的支持
  3. SparkSession创建方法:
    1. 交互式环境pyspark:在启动进入pyspark以后,pyspark就默认提供了SparkContext对象(名称为sc)和一个SparkSession对象(名称为spark)
    2. 独立程序
      from pyspark import SparkContext, SparkConf
      from pyspark.sql import SparkSession
      
      
      # 整个Spark SQL的指挥官
      spark = SparkSession.builder.config(conf=SparkConf()).getOrCreate()
  4. DataFrame的创建方法
    # 读取文本文件peopel.txt创建DataFrame
    df = park.read.text("people.txt")
    # 读取people.json文件创建DataFrame
    df = spark.read.json("people.txt")
    # 读取people.parquet文件创建DataFrame
    df = spark.read.parquet("people.parquet")
    
    # 读取文本文件people.json
    df = spark.read.format("text").load("people.txt")
    # 读取json文件people.json
    df = spark.read.format("json").load("people.json")
    # 读取Parquet文本people.parquet
    df = spark.read.format("parquet").load("people.parquet")
    
    # 将元素值显示出来
    df.show()
  5.  DataFrame的保存
    # 注意实际开发中文件路径需要些全称路径
    df.write.txt("people.txt")
    df.write.json("people.json")
    df.write.parquet("people.parquet")
    
    df.write.format("text").save("people.txt")
    df.write.format("json").save("people.json")
    df.write.format("parquet").save("peopel.parquet")
  6. DataFrame的创建和保存例子
    # 从示例文件people.json中创建DataFrame,保存到另外一个json文件中
    peopleDF = spark.read.format("json").load("file:///usr/local/spark/examples/src/main/resources/peopel.json")
    # 新生成一个名为newpeople.json的目录(不是文件)
    peopleDF.select("name", "age").write.format("json").save("file:///usr/local/spark/mycode/sparksql/newpeople.json")
    # 新生成一个名为newpeople.txt的目录(不是文件)
    peopleDF.select("name").write.format("text").save("file:///usr/local/spark/mycode/sparksql/newpeople.txt")

DataFrame的常用操作

df = spark.read.json("people.json")
# 打印DataFrame的模式信息
df.printSchema()
# 显示某些列的值
df.select(df["name"], df["age"] + 1).show()
# 过滤操作
df.filter(df["age"] > 20).show()
# 根据年龄分组groupBy()
df.groupBy("age").count().show()
# 排序sort
df.sort(df["age"].desc).show()    # 根据年龄降序
df.sort(df["age"].desc, df["name"].asc()).show()    # 先根据年龄降序,再根据姓名升序

从RDD格式数据转换成DataFrame

  1. 利用反射机制去推断RDD模式
    # 通过Row对象封装一行行数据
    from pyspark.sql import Row
    # 此时people是RDD实例对象
    people = spark.sparkContext.textFile("file:///usr/local/spark/examples/src/main/resources/people.txt").map(lambda line: line.split(",")).map(lambda p: Row(name=p[0], age=int(p[1])))
    # 转换成DataFrame
    schemaPeople = spark.createDataFrame(people)
    # 必须注册为临时表才能供下面的查询使用,此时参数people是临时表明名称
    schemaPeople.createOrReplaceTempView("people")
    # 查询得到的结果封装到DataFrame中
    personsDF = spark.sql("select name,age from people where age>20")
    # DataFrame中的每个元素都是一行记录,包含name和age两个字段,分别用p.name和p.age来获取值
    personsRDD = personsDF.rdd.map(lambda p: "Name:" + p.name + ", Age:" + str(p.age))
    personsRDD.foreach(print)

     

  2. 用编程方式去定义RDD模式(无法提前获知数据结构时)
    from pyspark.sql.types import *
    from pyspark.sql import Row
    
    
    # 下面生成"表头"
    schemaString = "name age"
    # StructField用来描述一个字段:字段名,字段类型
    fields = [StructField(field_name, StringType(), True) for field_name in schemaString.split(" ")]
    # 生成关系型数据库的模式
    schema = StructType(fields)
    # 下面生成表“表中的记录”
    lines = spark.sparkContext.textFile("file:///usr/local/spark/examples/src/main/resources/people.txt")
    parts = lines.map(lambda x: x.split(","))
    people = parts.map(lambda p: Row(p[0], p[1].strip()))
    # 下面把“表头”和“表中的记录”拼装在一起
    schemaPeople = spark.createDataFrame(people, schema)
    # 注册一个零时表供下面查询使用
    schemaPeople.createOrReplaceTempView("people")
    results = spark.sql("select name,age from people")
    results.show()

     

Spark SQL读写MySQL

  1. 安装MySQL:http://dblab.xmu.edu.cn/blog/install-mysql
  2. 下载MySQL的JDBC驱动程序:mysql-connector-java-51.40.tar.gz,放到/usr/local/spark/jars
  3. 启动交互式环境pyspark
    cd /usr/local/spark
    ./bin/pyspark
  4. 通过JDBC连接MySQL数据库
    1. 读取数据

      # 交互式环境pyspark
      jdbcDF = spark.read.format("jdbc").\
          option("driver", "com.mysql.jdbc.Driver").\
          option("url", "jdbc:mysql://localhost:3306/spark").\
          option("dbtable", "student").\
          option("user", "root").\
          option("password", "123456").\
          load()
      jdbcDF.show()
    2. 往spark库student表写入数据

      from pyspark.sql import Row
      from pyspark.sql.types import *
      from pyspark import SparkContext, SparkConf
      from pyspark.sql import SparkSession
      
      
      spark = SparkSession.builder.config(conf=SparkConf()).getOrCreate()
      
      # 设置模式信息(表头)
      schema = StructType([
          # 字段名,字段类型,是否可以为空
          StructField("id", IntegerType(), True),
          StructField("name", StringType(), True),
          StructField("gender", StringType(), True),
          StructField("age", IntegerType(), True)
      ])
      
      # 设置两条数据,表示两个学生的信息
      studentRDD = spark.sparkContext.parallelize(["3 Rongcheng M 26", "4 Guanhua M 27"]).map(lambda x: x.split(" "))
      
      # 创建Row对象,每个Row对象都是rowRDD中的一行
      rowRDD = studentRDD.map(lambda p: Row((p[0].split()), p[1].strip(), p[2].strip(), int(p[3].strip())))
      
      # 建立起Row对象和模式之间的对应关系,也就是把数据和模式(表头)对应起来
      studentDF = spark.createDataFrame(rowRDD, schema)
      
      # 写入数据库
      prop = {}
      prop["user"] = "root"
      prop['password'] = '123456'
      prop['driver'] = 'com.mysql.jdbc.Driver'
      studentDF.write.jdbc("jdbc:mysql://localhost:3306/spark", 'student', 'append', prop)

       

你可能感兴趣的:(spark)