3.SparkSQL

1.快速入门

sparkSQL是Spark的一个模块,用于处理海量结构化数据
限定:结构化数据处理

学习SparkSQL主要有2点:
1.SparkSQL本身十分优秀,支持SQL语言、性能强、可以自动优化、API简单、兼容HIVE等
2.企业大面积使用SparkSQL处理业务数据:离线开发、数仓搭建、科学计算、数据分析

特点
融合性:SQL可以无缝继承在代码中,随时用SQL处理数据
统一数据访问:一套标准API可读写不用数据源
Hive兼容:可以使用SparkSQL直接计算并生成Hive数据表
标准化连接:支持标准化JDBC、ODBC连接,方便和各种数据库进行数据交互

2.SparkSQL概述

SparkSQL和Hive的异同
同:都运行在YARN上,都是分布式SQL计算引擎
异:
Spark:内存计算,无元数据管理、SQL\代码混合执行、底层运行SparkRDD
Hive:磁盘迭代、Metastore、仅能以SQL开发、底层运行MapReduce

SparkSQL的数据抽象
pandas - DataFrame:二维表结构、本地集合
SparkCore - RDD:无标准数据结构,分布式集合
SparkSQL - DataFrame:二维表结构、分布式集合

SparkSession对象
在RDD阶段,程序的执行入口对象是SparkContext
在Spark2.0后推出了SparkSession对象,作为Spark编码的统一入口对象

SparkSession对象可以:用于SparkSQL编程作为入口对象;用于SparkCore编程,可以通过SparkSession获取到SparkContext

SparkSQL测试

from pyspark.sql import SparkSession

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    # appName设置程序名称,config设置一些常用属性
    # 最后通过getOrCreate()方法 创建SparkSession对象

    # 通过SparkSession对象,回去SparkContext对象
    sc = spark.sparkContext

    # sparkSQL的Helloword
    df = spark.read.csv('stu_score.txt',sep=',',header=False)
    df2 = df.toDF("id","name","score")
    df2.printSchema()
    df2.show()

    df2.createTempView("score")

    # SQL 风格
    spark.sql("""
        SELECT * FROM score WHERE name='语文' LIMIT 5
    """).show()

    # DSL风格
    df2.where("name='语文'").limit(5).show()

3.DataFrame

3.1 DataFrame的组成

DataFrame是一个二维表结构(行、列、表结构描述)

在结构层面上:
StructType对象描述整个DataFrame的表结构
StructField对象描述一个列的信息

在数据层面:
Row对象记录一行数据
Column对象记录一列数据并包含列的信息

3.2 DataFrame构建代码

基于RDD的方式1
DataFrame可以从RDD转换而来,都是分布式数据集,其实就是内部存储的结构,转换为二维表结构

from pyspark.sql import SparkSession

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 基于RDD转换成DataFrame
    # 数据示例:Alen,18
    rdd = sc.textFile("people.txt").map(lambda x: x.split(',')).\
        map(lambda x: (x[0], int(x[1])))

    # 构建DataFrame对象
    # 参数1 被转换的RDD
    # 参数2 指定列名,通过list的i形式指定,按照规定依次提供字符串名称
    df = spark.createDataFrame(rdd, schema=["name","age"])

    # 打印DataFrame的表结构
    df.printSchema()

    # 打印df中的数据
    # 参数1 表示展示数据条数,默认20
    # 参数2 表示是否队列进行截断,如果列的数据长度超过20个字符长度,后续的内容以...代替
    # 如果给False 表示不截断, 默认True
    df.show(20, False)

    # 将DF对象转换成临时是图标,可供sql语句查询
    df.createOrReplaceTempView("people")
    spark.sql("SELECT * FROM people WHERE age < 30").show()

基于RDD的方式2
通过StructType对象来定义DataFrame的表结构转换RDD

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 基于RDD转换成DataFrame
    # 数据示例:Alen,18
    rdd = sc.textFile("people.txt").map(lambda x: x.split(',')).\
        map(lambda x: (x[0], int(x[1])))

    # 构建表结构的描述对象:StructType对象
    schema = StructType().add("name", StringType(), nullable=True).add("age", IntegerType(), nullable=False)

    # 基于StructType对象取构建RDD到DF的转换
    df = spark.createDataFrame(rdd, schema=schema)

    df.printSchema()
    df.show()

基于RDD的方式3
使用RDD的toDF方法转换RDD

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 基于RDD转换成DataFrame
    # 数据示例:Alen,18
    rdd = sc.textFile("people.txt").map(lambda x: x.split(',')).\
        map(lambda x: (x[0], int(x[1])))

    # toDF的方式构建DataFrame
    df1 = rdd.toDF(["name","age"])
    df1.printSchema()
    df1.show()

    # toDF的方式2 通过StructType构建
    schema = StructType().add("name", StringType(), nullable=True).add("age", IntegerType(), nullable=False)

    df2 = rdd.toDF(schema=schema)
    df2.printSchema()
    df2.show()

基于Pandas的DataFrame
将Pandas的DataFrame对象,转变为分布式的SparkSQL DataFrame对象

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 基于Pandas的DataFrame构建SparkSQL的DataFrame对象
    pdf = pd.DataFrame({
        {
            "id": [1, 2, 3],
            "name": ["张大仙", "王晓晓", "吕不为"],
            "age": [11, 22, 13]
        }
    })

    df = spark.createDataFrame(pdf)

    df.printSchema()
    df.show()

读取外部数据
通过SparkSQL的统一API进行数据读取构建DataFrame
统一API示例代码:

sparksession.read.format("text|csv|json|parquet|orc|avro|jdbc|...")
    .option("K", "V") # option可选
    .schema(StructType|String) # STRING的语法如,schema("name STRING", "age INT")
    .load("被读取文件的路径,支持本地文件系统的HDFS")

读取txt数据源

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 构建StructType, text数据源,读取数据的特点是,将一整行只作为”一个列“读取,默认列名是value 类型是string
    schema = StructType().add("data", StringType(), nullable=True)
    df = spark.read.format("text").schema(schema=schema).load("people.txt")

    df.printSchema()
    df.show()

读取json数据源
使用format(“json”)读取json数据
示例代码:

df = spark.read.format("json").load("people.json")
# JSON类型一般不用写schema,json自带,json带有列名和列类型(字符串和数字)
df.printSchema()
df.show()

读取csv数据源
使用format(“csv”)读取csv数据
示例代码:

df = spark.read.format("csv") \
    .option("sep",";") \  
    .option("header",False) \
    .option("encoding","utf-8") \
    .schema("name STRING, age INT, job STRING") \
    .load("people.csv")

读取parquet数据源
使用format(“parquet”)读取parquet数据
parquet:是spark中常见的一种列式存储文件格式
和Hive中的ORC差不多,都是列存储格式

parquet对比普通的文本文件的区别:
parquet内置schema(列名\列类型\是否为空)
存储是以列作为存储格式
存储是序列化存储在文件中的

# parquet 自带schema,直接load啥也不需要
df = spark.read.format("parquet").load("users.parquet")

3.3 DataFrame的入门操作

DataFrame支持两种风格进行编码分别是DSL和SQL

DSL风格
DSL称为领域特定语言
指定DataFrame的特有API
DSL风格意思是以API调用的方式来处理数据
比如:df.where().limit()

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    df = spark.read.format("csv").schema("id INT, subject STRING, score INT").load("stu_score.txt")

    # Column对象的获取
    id_column = df['id']
    subject_column = df['subject']

    # DLS风格
    df.select(["id","subject"]).show()
    df.select("id","subject").show()
    df.select(id_column,subject_column).show()

    # filter API
    df.filter("score < 99").show()
    df.filter(df["score"] < 99).show()

    # where API
    df.where("score < 99").show()
    df.where(df["score"] < 99).show()

    # groupBy
    df.groupBy("subject").count().show()
    df.groupBy(df["subject"]).count().show()

    # df.groupBy API的返回值GroupeedData(一个有分组关系的数据结构,使用一些API对分组做聚合)

    r = df.groupBy("subject")
    print(type(r))
    # 使用聚合方法后返回的才是DataFrame
    r.sum().show()

SQL语法风格
SQL风格就是使用SQL语句处理DataFrame的数据
比如:spark.sql(“SELECT * FROM XXX”)

如果想要使用SQL风格的语法,需要将DataFrame注册成表:

df.createTempView("score") # 注册一个临时视图
df.createOrReplaceTempView("score") # 注册一个临时表,如果存在进行替换
df.createGlobalTempView("score") # 注册一个全局表

全局表:跨SparkSession对象使用,在一个程序内的多个SparkSession中均可调用,查询带上前缀:global_temp.

临时表:只在当前SparkSession中可用

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    df = spark.read.format("csv").schema("id INT, subject STRING, score INT").load("stu_score.txt")

    # 注册临时表
    df.createTempView("score")
    df.createOrReplaceTempView("score_2")
    df.createGlobalTempView("score_3")

    # 可以通过SparkSession对象的sql api来完成sql语句的执行
    spark.sql("SELECT subject, COUNT(*) AS cnt FROM score GROUP BY subject").show()
    spark.sql("SELECT subject, COUNT(*) AS cnt FROM score_2 GROUP BY subject").show()
    spark.sql("SELECT subject, COUNT(*) AS cnt FROM global_temp.score_3 GROUP BY subject").show()

pyspark.sql.functions包
这个包里提供了一系列的计算函数供SparkSQL使用

导包
from pyspark.sql import functions as F

3.4 词频统计案例练习

单词计数需求,使用DSL和SQL两种风格实现
在实现的过程中,会出现新的API,边写边学

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType
from pyspark.sql import functions as F

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # TODO 1: SQL风格进行处理
    rdd = sc.textFile("words.txt").flatMap(lambda x: x.split(" ")).map(lambda x: [x])

    df = rdd.toDF(["word"])

    # 注册DF为表
    df.createTempView("words")

    spark.sql("SELECT word, COUNT(*) AS cnt FROM words GROUP BY word ORDER BY  cnt DESC").show()

    # TODO 2:DSL 风格处理
    df = spark.read.format("text").load("words.txt")

    # withColumn方法
    # 方法功能:对已存在的列进行操作,返回一个新的列,如果名字和老列相同,那么替换,否则作为新列存在
    df2 = df.withColumn("value", F.explode(F.split(df['value'], " ")))
    df2.groupBy("value","word")\
        .count()\
        .withColumnRenamed("count","cnt")\
        .orderBy("cnt", ascending=False)\
        .show()

3.5 电影频分数据分析案例

MovieLens数据集,包含多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息

需求:
1.查询用户平均分
2.查询电影平均分
3.查询大于平均分的电影数量
4.查询高分电影中(>3)打分次数最多的用户,并求出此人打的平均分
5.查询每个用户的平均打分,最低打分,最高打分
6.查询被评分超过100次的电影的平均分排名TOP10

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType
from pyspark.sql import functions as F

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 1.读取数据集
    schema = StructType().add("user_id", StringType(), nullable=True)\
        .add("movie_id", IntegerType(), nullable=True)\
        .add("rank", IntegerType(), nullable=True)\
        .add("ts", StringType(), nullable=True)
    df = spark.read.format("csv")\
        .option("sep","\t")\
        .option("header", False)\
        .option("encoding", "utf-8")\
        .schema(schema=schema)\
        .load("u.data")

    # TODO 1:用户评分
    df.groupBy("user_id").avg("rank")\
        .withColumnRenamed("avg(rank)","avg_rank")\
        .withColumn("avg_rank", F.round("avg_rank", 2))\
        .orderBy("avg_rank", ascending=False).show()

    # TODO 2: 电影平均分查询
    df.createTempView("movie")
    spark.sql("""
        SELECT movie_id, ROUND(AVG(rank), 2) AS avg_rank FROM movie GROUP BY avg_rank DESC
    """).show()

    # TODO 3: 查询大于平均分的电影数量
    print("大于平均分电影的数量:", df.where(df['rank'] > df.select(F.avg(df['rank'])).first()['avg(rank)']).count())

    # TODO 4: 查询高分电影中(>3)打分次数最多的用户,此人打分的平均分
    # 先找人
    user_id = df.where("rank > 3") \
        .groupBy("user_id") \
        .count() \
        .withColumnRenamed("count","cnt") \
        .orderBy("cnt", ascending=False) \
        .limit(1).first()["user_id"]

    # 计算这个人的打分平均分
    df.filter(df['user_id'] == user_id).select(F.round(F.avg("rank"), 2)).show()

    # TODO 5:查询每个用户的平均打分,最低打分,最高打分
    # agg里可以写多个聚合
    df.groupBy("user_id") \
        .agg(
            F.round(F.avg("rank"), 2).alias("avg_rank"),
            F.min("rank").alias("min_rank"),
            F.max("rank").alias("max_rank")
        ).show()

    # TODO 6:查询平均分超过100次的电影的平均分及排名TOP10
    df.groupBy("movie_id") \
        .agg(
            F.count("movie_id").alias("cnt"),
            F.round(F.avg("rank"), 2).alias("avg_rank")
        ).where("cnt > 100").orderBy("avg_rank",ascending=False).limit(10).show()

新的API

  1. agg:它是GroupedData对象的API,作用是在里面可以写多个聚合
  2. alias:它是Column对象的API,可以针对一个列进行改名
  3. withColumnRenamed:它是DataFrame的API,可以对DF中的列进行改名,一次改一个列,该多个列可以链式调用
  4. orderBy:DataFrame的API,进行排序,参数1是被排序的列,参数2是升序(True) 或 降序(False)
  5. first:DataFrame的API,取出DF的第一行数据,返回值结果是Row对象(就是一个数组)

3.6 SparkSQL Shuffle分区数目

在SparkSQL中当Job中产生Shuffle时,默认的分区数(spark.sql.shuffle.partitions)为200,在实际项目中要合理设置

设置:

1. 配置文件:conf/spark-defaults.conf: spark.sql.shuffle.partitions 100
2. 在客户端提交参数:bin/spark-submit --conf "spark.sql.shuffle.partitions=100"
3. 在代码中可以设置:spark = SparkSession.builder.\
                            appName("create df").\
                            master("local[*]").\
                            config("spark.sql.shuffle.partitions", "2").\
                            getOrCreate()

3.7 SparkSQL 数据清洗API

去重方法dropDuplicates
功能:对DF的数据进行去重,如果重复数据有多条,取第一条

# 去重API dropDuplicates,无参数时对数据进行整体去重
df.dropDuplicates().show()
# API 同样可以针对字段进行去重,如下传入age字段,表示只要年龄一样,就认为是重复数据
df.dropDuplicates(['age','job'].show()

缺失值处理

# dropna API可以对缺失值的数据进行删除
# 无参数使用,只要列中有null就删除这一行
df.dropna().show()
# thresh = 3 表示最少满足3个有效列,不满足就删除当前行
df.dropna(thresh=3).show()
df.dropna(thresh=2,subset=['name','age']).show()

# 也可以填充缺失值
# DataFrame的fillna 对缺失的列进行填充
df.fillna("loss").show()

# 指定列填充
df.fillna("N/A", subset=['job']).show()

# 设定一个字典,对所有的列提供填充规则
df.fillna({"name":"未知姓名", "age": 1, "jon":"worker"}).show()

3.8 DataFrame数据写出

SparkSQL统一API写出DataFrame数据
统一API语法

df.write.mode().format().option(K, V).save(PATH)
# mode 传入模式,可选:append 追加,overwrite 覆盖,ignore 忽略,error 重复就报错(默认)
# format 传入格式字符串,可选:text, csv, json, parquet, orc, avro, jdbc
# 注意text源只支持单列df写出
# option 设置属性,如 .option("seq",",")
# save 写出的路径,支持本地和HDFS

3.9 DataFrame通过JDBC读写数据库

读取JDBC是需要有驱动的,读取jdbc:mysql://这个协议,需要mysql的驱动jar包给spark使用

# 写DF通过JDBC到数据库中
df.write.mode("overwrite").\
    format("jdbc").\
    option("url","jdbc:mysql://node1:3306/test?useSSL=false&useUnicode=true").\
    option("dbtable","u_data").\
    option("user","root").\
    option("password","123456").\
    save()

注意:

  • jdbc连接字符串中,建议使用useSSL=false确保连接可以正常连接
  • jdbc连接字符串中,建议使用useUnicode=true 来确保传输中不出现乱码
  • save() 不要填充参数,没有路径,是写出数据库
  • dbtable属性:指定写出的表名

4.SparkSQL函数定义

4.1 SparkSQL定义UDF函数

在SparkSQL中,目前仅支持UDF函数和UDAF函数,目前python仅支持UDF

定义方式
1.sparksession.udf.register()
注册的udf可以用于DSL和SQL,返回值用于DSL风格,传参内给的名字用于SQL风格

2.pyspark.sql.functions.udf
仅能用于DSL风格

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType
from pyspark.sql import functions as F

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 创建一个RDD
    rdd = sc.parallelize([1,2,3,4,5,6,7]).map(lambda x: [x])
    df = rdd.toDF(["num"])

    # TODO 1: 方式1 sparksession.udf.register(), DSL和SQL风格均可以使用
    # UDF的处理函数
    def num_ride_10(num):
        return num * 10

    # 参数1:注册UDF的名称,仅可用于SQL风格
    # 参数2:UDF的处理逻辑,是一个单独的方法
    # 参数3:声明UDF的返回值类型,注意:UDF注册时必须声明返回值类型,并且UDF的真实返回值一定要和声明的返回值一致
    # 返回值对象:这是一个UDF对象,仅可以用于DSL语法
    # 当前这种方式定义的UDF,可以通过参数1的名称用于SQL风格,通过返回值对象用户DSL风格
    udf2 = spark.udf.register("udf1", num_ride_10, IntegerType())

    # SQL风格使用
    # selectExpr 以SELECT的表达式执行,表达式SQl风格的表达式
    # select方法,接受普通字符串字段名,或返回Column对象的计算
    df.selectExpr("udf1(num)").show()

    # DSL风格中使用
    # 返回值UDF对象 如果作为方法使用,传入的参数一定是Column对象
    df.select(udf2(df['num'])).show()

    # TODO 2: 方式2注册,仅能用于DSL风格
    udf3 = F.udf(num_ride_10, IntegerType())
    df.select(udf3(df['num'])).show()

注:返回值也可以是复杂数据结构

UDAF

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType
from pyspark.sql import functions as F

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    # 创建一个RDD
    rdd = sc.parallelize([1,2,3,4,5,6,7],3).map(lambda x: [x])
    df = rdd.toDF(["num"])

    # 这种的方式,使用RDD的mapPartitions算子来完成聚合操作
    # 如果用mapPartitions API 完成UDAF聚合,一定要单分区
    single_partition_rdd = df.rdd.repartition(1)

    def process(iter):
        sum = 0
        for row in iter:
            sum += row['num']

        return [sum] # 一定要list

    print(single_partition_rdd.mapPartitions(process).collect())

4.2 SparkSQL窗口函数

from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StringType, IntegerType
from pyspark.sql import functions as F

if __name__ == '__main__':
    # 构建SparkSession对象,这个对象是构建器模式 通过builder方法来构建
    spark = SparkSession.builder.appName("test").master("local[*]").\
        config("spark.sql.shuffle.partitions",'4').getOrCreate()
    sc = spark.sparkContext

    rdd = sc.parallelize([
        ('张三', 'class_1',99),
        ('王五', 'class_2', 35),
        ('李四', 'class_3', 17),
        ('赵六', 'class_1', 66),
        ('王丽', 'class_4', 33),
        ('王军', 'class_1', 22)
    ])

    schema = StructType().add("name", StringType()).add("class",StringType()).add("score", IntegerType())
    df = rdd.toDF(schema)

    df.createTempView("stu")

    # TODO 聚合窗口函数的演示
    spark.sql("""
        SELECT *, AVG(score) OVER() AS avg_score FROM stu
    """).show()
    # TODO 排序相关的窗口函数计算
    # RANK over, DENSE_RANK over, ROW_NUMBER over
    spark.sql("""
        SELECT *, ROW_NUMBER() OVER(ORDER BY score DESC) AS row_number_rank,
        DENSE_RANK() OVER(PARTITION BY class ORDER BY score DESC) AS dense_rank,
        RANK() OVER(ORDER BY score) AS rank
        FROM stu
    """).show()

    # TODO NTILE
    # NTILE 均分6份
    spark.sql("""
        SELECT *, NTILE(6) OVER(ORDER BY score DESC) FROM stu
    """).show()

5.SparkSQL的运行流程

5.1 SparkSQL的自动优化

RDD的而运行会完全按照开发者的代码执行,如果开发者水平有限,RDD的执行效率会受到影响,而SparkSQL会对写完的代码,执行“自动优化”,以提升代码运行效率,避免开发者水平影响到代码执行效率

DataFrame是二维表结构,可以针对优化
SparkSQL自动优化依赖于Catalyst优化器
为了解决过多依赖Hive的问题,SparkSQL使用了一个新的SQL优化器代替Hive中的优化器,这个优化器就是Catalyst(和sql优化类似)

catalyst的优点:
1.谓词下推、断言下推:将逻辑判断提前到前面,以减少shuffle阶段的数据量
2.列值裁剪:将加载的列进行裁剪,尽量减少被处理数据的宽度

6.Spark on Hive

原理
原理就是spark能够连接上Hive的MetaStore

条件:
1.MetaStoer需要存在并开机
2.Spark知道MetaStore在哪里(IP 端口)

配置
在spark的conf目录中,创建hive-site.xml


    
    
        hive.metastore.warehouse.dir
        /usr/hive/warehouse
    
    
        hive.metastore.local
        false
    
    
    
        hive.metastore.uris
        thrift://node1:9083
    

在代码中集成Spark On Hive
前提:确保MetaStore服务是启动好的

spark = SparkSession.builder\
    .appName("create df")\
    .master("local[*]")\
    .config("spark.sql.shuffle.paratitions","4")\
    .config("spark.warehouse.dir","hdfs://node1:8020/user/hive/warehouse")\
    .config("hive.metastore.uris","thrift://node1:9083")\
    .enableHiveSupport().getOrCreate()

spark.sql("""SELECT * FROM test.student""").show()

7.分布式SQL执行引擎

7.1 概念

Spark中有一个服务叫做:ThriftServer服务,可以启动并监听10000端口
这个服务对外提供功能,可以用数据库工具或代码连接上,直接写sql操作spark

7.2 配置

1.确保已经配置好Spark On Hive
2.启动ThriftServer

$SPARK_HOME/sbin/start-thriftserver.sh \
--hiveconf hive.server2.thrift.port=10000 \
--hiveconf hive.server2.thrift.bind.host=node1 \
--master local[2]
# master选择local,每条sql都是local进程执行
# master选择yarn,每条sql都在YARN集群中执行

你可能感兴趣的:(spark)