深入解析 SparkSQL:从基础到实践与优化

目录

[一、SparkSQL 详解](#一、SparkSQL 详解)

[一)Spark 的组成部分](#一)Spark 的组成部分)

[二)SparkSQL 的由来](#二)SparkSQL 的由来)

[三)SparkSQL 的技术定义与应用场景](#三)SparkSQL 的技术定义与应用场景)

[四)SparkSQL 与 SparkCore 对比](#四)SparkSQL 与 SparkCore 对比)

[二、SparkSQL 编程实战](#二、SparkSQL 编程实战)

1、第一个综合案例:

2、使用sparkSQL编写wordCount

3、使用SparkSQL中的DSL操作WordCount

三、优化技巧

四、总结


在大数据处理领域,Spark 作为一款强大的开源分布式计算框架,占据着举足轻重的地位。而 SparkSQL 作为 Spark 生态系统中专门针对结构化数据计算设计的关键模块,更是广泛应用于各类数据处理场景,无论是离线数据分析、实时流计算,还是机器学习任务前的数据预处理等环节,都发挥着不可替代的作用。本文将带大家全面认识 SparkSQL,从其基本概念、技术由来,到编程实战以及优化技巧,一步步揭开它的神秘面纱。

一、SparkSQL 详解

一)Spark 的组成部分

Spark 整体架构丰富且精妙,主要包含 SparkCore、SparkSQL、Spark Streaming、MLlib(机器学习库)以及 GraphX(图计算)等部分。SparkCore 作为核心基础,提供了分布式任务调度、内存管理、错误恢复等基础能力,是整个 Spark 运行的基石;SparkSQL 专注于结构化数据处理,赋予使用者类似 SQL 操作的便捷性;Spark Streaming 支持实时数据流处理,能对源源不断流入的数据进行高效分析;MLlib 集成众多机器学习算法,助力大数据场景下的模型训练与预测;GraphX 则应对图结构数据,实现图相关的计算与分析操作。

二)SparkSQL 的由来

早期使用 SparkCore 编写程序应对结构化数据时,过程繁琐复杂,鉴于日常处理数据大多是结构化形式(像常见的 JSON、CSV、TSV、parquet、orc 这类结构化文件,以及 MySQL、Oracle、Hive 中的数据库表数据),而 SQL 语言天然适配结构化数据处理场景,于是 SparkSQL 应运而生。

结构化的文件:JSON、CSV【以逗号分隔】、TSV【以制表符分隔】、parquet、orc

结构化的表:数据库中表的数据:MySQL、Oracle、Hive

对比:同样是进行wordcount统计,使用sparkCore的写法:

使用SparkSQL编写:

SparkSQL 它的sql语句跟hive sql很像,跟我们的mysql的语句也非常像,它可以将SQL直接转换为SparkCore进行计算。

将一些结构化的数据进行sql查询,需要将数据变为表,是表就必须有表结构,表结构就是Schema。

问题:SparkCore只能通过普通Python/Scala/Java代码编程的方式 来使用,对于统计分析的需求不是特别的友好怎么办?

解决:一群大数据开发者,将Hive源码直接独立出来,将底层解析成MapReduce的代码替换成解析变成SparkCore,这个是Shark:鲨鱼技术的由来。

Shark源码是依赖于Hive和Spark,只要Hive或者Spark一旦发生更新,Shark就必须更新。

解决:DataBricks团队将Shark的源代码重构,开发了SparkSQL模块,融合到了Spark中。

**记住的:**PySpark是看不到DataSet,PySpark只能看到DataFrame,底层都是DataSet。

DataFrame = 数据 + Schema

三)SparkSQL 的技术定义与应用场景

  1. 定义:SparkSQL 立足 SparkCore 之上,专为结构化数据计算量身定制,同时对半结构化数据(如 JSON)处理也游刃有余。它对外提供 SQL 和 DSL(领域特定语言)开发接口,能够无缝将 SQL 或 DSL 语句转化为 SparkCore 程序,高效处理数据。其语法与 Hive SQL 兼容性超 90%,学习成本低且上手快。
  2. 应用场景
    • 离线计算:深度集成 Hive,在离线数据仓库数据计算环节大显身手,涵盖数据分析、数据分层转换等任务,像电商项目数据处理中可取代 Presto,高效完成诸如按日、月统计销售数据,分析用户行为路径等复杂分析。
    • 实时计算:依托 StructStreaming(结构化流计算),基于 SparkSQL 编写代码实现实时流数据处理,重合度高达 90%。比如监控电商网站实时订单流,统计每秒订单量、实时热门商品销量排名等。
    • 机器学习:与 SparkMLlib 协同,基于 SparkSQL 编程构建机器学习流程,先利用 SparkSQL 清洗、转换数据(如特征工程里对数值型特征归一化、类别型特征编码等),为后续模型训练筑牢基础。

四)SparkSQL 与 SparkCore 对比

SparkCore 侧重底层基础能力构建,以 RDD(弹性分布式数据集)为核心抽象,编程需借助复杂代码逻辑操控 RDD 实现数据处理,像手动编写分区、转换、聚合等逻辑;而 SparkSQL 借助 SQL 或 DSL 语法,使用者无需过度关注底层分布式细节,专注数据逻辑表达,开发效率大幅提升。打个比方,统计一组销售数据里各地区销售额,用 SparkCore 要历经复杂 RDD 转换,用 SparkSQL 简单几条 SQL 语句结合分组聚合函数轻松搞定。

二、SparkSQL 编程实战

pyspark --master local[*] 敲回车

pyspark --master spark://bigdata01:7077 standalone模式

pyspark --master yarn yarn模式

开发步骤:

step1:在Python代码中构建驱动对象:SparkSession

step2:读取数据变成DataFrame,使用SQL或者DSL进行处理,保存处理好的结果

step3:关闭驱动对象:stop

如何构建SparkSession:

python 复制代码
from pyspark.sql import SparkSession

if __name__ == '__main__':
	sparkSession = SparkSession.builder.master("local[2]").appName("第一次构建SparkSession").config("spark.sql.shuffle.partitions",2).getOrCreate()

编写sql:

bash 复制代码
step1:先将DataFrame注册成一张临时的视图【只读的表】
df.createOrReplaceTempView("people")
step2:使用SQL对临时视图进行处理,得到新的DataFrame
sqlDF = spark.sql("SELECT * FROM people")
sqlDF.show()

# +----+-------+
# | age|   name|
# +----+-------+
# |null|Michael|
# |  30|   Andy|
# |  19| Justin|

编写DSL:

bash 复制代码
类似于RDD的编程方式:调用算子函数来实现处理
流程:直接调用DataFrame的DSL函数进行处理原生DSL函数【将SQL语法变成了函数】:select、where、
groupBy、orderBy、limit、count、agg
比写SQL更灵活:不用考虑SQL执行顺序部分RDD算子函数:repartition、coalesce、persist、
unpersist、checkpoint

使用:
# 打印表结构
df.printSchema()
# root
# |-- age: long (nullable = true)
# |-- name: string (nullable = true)

df.select("name").show()
# +-------+
# |   name|
# +-------+
# |Michael|
# |   Andy|
# | Justin|
# +-------+
df.select(df['name'], df['age'] + 1).show()
# +-------+---------+
# |   name|(age + 1)|
# +-------+---------+
# |Michael|     null|
# |   Andy|       31|
# | Justin|       20|
# +-------+---------+

df.where(df['age'] > 21).show()
# +---+----+
# |age|name|
# +---+----+
# | 30|Andy|
# +---+----+

df.groupBy("age").count().show()
# +----+-----+
# | age|count|
# +----+-----+
# | 19|   1|
# |null|   1|
# | 30|   1|

1、第一个综合案例:

bash 复制代码
{"name":"Michael"}
{"name":"Andy", "age":30}
{"name":"Justin", "age":19}
python 复制代码
import os

from pyspark.sql import SparkSession

if __name__ == '__main__':
    #获取sparkSession对象
	# 设置 任务的环境变量
	os.environ['JAVA_HOME'] = r'C:\Program Files\Java\jdk1.8.0_77'
	# 配置Hadoop的路径,就是前面解压的那个路径
	os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1/hadoop-3.3.1'
	# 配置base环境Python解析器的路径
	os.environ['PYSPARK_PYTHON'] = r'C:\ProgramData\Miniconda3\python.exe'  # 配置base环境Python解析器的路径
	os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'
	# 得到sparkSession对象
	spark = SparkSession.builder.master("local[2]").appName("第一个sparksql案例").config("spark.sql.shuffle.partitions",2).getOrCreate()
	# 处理逻辑
	print(spark)
	# 读取外部数据,获取dataframe
	df = spark.read.json("../../datas/sql/person.json")
	print(df) # DataFrame类型
	# 给数据集起个名字,该名字随便起
	df.createOrReplaceTempView("person")
	# 通过sql 获取想要的数据,返回值是一个DataFrame
	df2 = spark.sql("select name,age from person")
	# 想要展示dataframe中的数据,可以选择使用show 方法,列名和数据一起展示
	df2.show()
	"""
	+-------+----+
	|Michael|null|
	|   Andy|  30|
	| Justin|  19|
	+-------+----+
	"""
	# 使用DSL 写法操作数据
	"""
	root
		 |-- age: long (nullable = true)
		 |-- name: string (nullable = true)
	"""
	df.printSchema() # 打印一个dataFrame的表结构
	df.select("name").show()
	df.select("name","age").show()

	df.where(df['age'] > 20).show()
	# 分组并统计
	df.groupby("name").sum("age").show()
	# select age,count(1) from 表名 group by age order by age limit 2
	df.groupby("age").count().orderBy("age",ascending=False).limit(2).show()

	# 以上这些都是dsl写法,能不能使用sql写法呢?
	spark.sql("select age,count(1) from person group by age order by age desc limit 2").show()

	# 关闭
	spark.stop()

2、使用sparkSQL编写wordCount

将经常使用的代码变为一个模板:

python 复制代码
from pyspark.sql import SparkSession
import os

"""
------------------------------------------
  Description : TODO:
  SourceFile : ${NAME}
  Author  : 
  Date  : ${DATE}
-------------------------------------------
"""

if __name__ == '__main__':
	# 构建环境变量
	# 配置环境
	os.environ['JAVA_HOME'] = 'D:/Program Files/Java/jdk1.8.0_271'
	# 配置Hadoop的路径,就是前面解压的那个路径
	os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1/hadoop-3.3.1'
	# 配置base环境Python解析器的路径
	os.environ['PYSPARK_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'  # 配置base环境Python解析器的路径
	os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'

	# 获取sparkSession对象
	spark = SparkSession.builder.master("local[2]").appName("第一次构建SparkSession").config(
		"spark.sql.shuffle.partitions", 2).getOrCreate()
	

	spark.stop()
python 复制代码
import os

from pyspark.sql import SparkSession


if __name__ == '__main__':
	# 获取sparkSession对象
	# 设置 任务的环境变量
	os.environ['JAVA_HOME'] = r'C:\Program Files\Java\jdk1.8.0_77'
	# 配置Hadoop的路径,就是前面解压的那个路径
	os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1/hadoop-3.3.1'
	# 配置base环境Python解析器的路径
	os.environ['PYSPARK_PYTHON'] = r'C:\ProgramData\Miniconda3\python.exe'  # 配置base环境Python解析器的路径
	os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'
	# 得到sparkSession对象
	spark = SparkSession.builder.master("local[2]").appName("使用sparksql编写wordcount").config("spark.sql.shuffle.partitions", 2).getOrCreate()
	# 处理逻辑
	df = spark.read.text("../../datas/data.txt")
	df.createOrReplaceTempView("wordcount")
	# 假如一个数据没有列名,默认列名为value
	# show(truncate=False) 默认显示20个 字符,超过的变为...,假如想全部显示,可以写成truncate=False
	spark.sql("""
	    with t as (
	       select word from wordcount lateral view explode(split(value," ")) a as word
	        where trim(word) !=''
	    )
	    select word,count(1) from t group by word order by count(1) desc
	""").show(truncate=False)

	spark.sql("""
		    with t as (
		       select explode(split(value," ")) as word from wordcount
		    )
		    select word,count(1) from t where trim(word)!='' group by word order by count(1) desc
		""").show(truncate=False)

	# 关闭
	spark.stop()

3、使用SparkSQL中的DSL操作WordCount

python 复制代码
import os

from pyspark.sql import SparkSession
import pyspark.sql.functions as F

if __name__ == '__main__':
	# 获取sparkSession对象
	# 设置 任务的环境变量
	os.environ['JAVA_HOME'] = r'C:\Program Files\Java\jdk1.8.0_77'
	# 配置Hadoop的路径,就是前面解压的那个路径
	os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1/hadoop-3.3.1'
	# 配置base环境Python解析器的路径
	os.environ['PYSPARK_PYTHON'] = r'C:\ProgramData\Miniconda3\python.exe'  # 配置base环境Python解析器的路径
	os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'
	# 得到sparkSession对象
	spark = SparkSession.builder.master("local[2]").appName("DSL版本的wordcount").config("spark.sql.shuffle.partitions", 2).getOrCreate()
	# 处理逻辑
	df = spark.read.text("../../datas/data.txt")
	# DSL的第一种写法
	# 第一种写法
	df.select(F.explode(F.split("value"," ")).alias("word")).where(F.trim(F.col("word")) != '')\
	 .groupby(F.col("word")).count().alias("count").orderBy(F.col("count"),ascending=False)\
	 .show()
	# 第二种写法
	df.select(F.explode(F.split("value", " ")).alias("word")).where(F.trim(F.col("word")) != '') \
		.groupby(F.col("word")).agg(F.count(F.col("word")).alias("count")).orderBy(F.col("count"), ascending=False) \
		.show()

	# 关闭
	spark.stop()

三、优化技巧

在 SparkSQL 编程中,合理优化能显著提升性能与资源利用率。利用with语句结合SparkSession(或SparkContext)的魔法方法,能确保资源自动管理与释放,示例如下:

python 复制代码
import os

from pyspark.sql import SparkSession
import pyspark.sql.functions as F

if __name__ == '__main__':
	# 获取sparkSession对象
	# 设置 任务的环境变量
	os.environ['JAVA_HOME'] = r'C:\Program Files\Java\jdk1.8.0_77'
	# 配置Hadoop的路径,就是前面解压的那个路径
	os.environ['HADOOP_HOME'] = 'D:/hadoop-3.3.1/hadoop-3.3.1'
	# 配置base环境Python解析器的路径
	os.environ['PYSPARK_PYTHON'] = r'C:\ProgramData\Miniconda3\python.exe'  # 配置base环境Python解析器的路径
	os.environ['PYSPARK_DRIVER_PYTHON'] = 'C:/ProgramData/Miniconda3/python.exe'
	# 得到sparkSession对象
	with SparkSession.builder.master("local[2]").appName("DSL版本的wordcount").config("spark.sql.shuffle.partitions", 2).getOrCreate() as spark:
		# 处理逻辑
		df = spark.read.text("../../datas/data.txt")
		# DSL的第一种写法
		# 第一种写法
		df.select(F.explode(F.split("value"," ")).alias("word")).where(F.trim(F.col("word")) != '')\
		 .groupby(F.col("word")).count().alias("count").orderBy(F.col("count"),ascending=False)\
		 .show()
		# 第二种写法
		df.select(F.explode(F.split("value", " ")).alias("word")).where(F.trim(F.col("word")) != '') \
			.groupby(F.col("word")).agg(F.count(F.col("word")).alias("count")).orderBy(F.col("count"), ascending=False) \
			.show()

这种写法避免资源泄露,让代码更简洁、健壮,尤其在复杂数据处理流程里,多次创建销毁SparkSession场景下优势尽显,提升代码整体质量与执行效率。

四、总结

SparkSQL 凭借简洁语法、强大功能、广泛应用场景,已然成为大数据处理利器。从基础概念理解、编程实战演练到优化技巧掌握,是用好它的必经之路。希望本文能助力大家在大数据分析征程中,灵活运用 SparkSQL 攻克各类结构化数据处理难题,挖掘数据深层价值,提升业务洞察与决策能力。后续大家可深入探索 SparkSQL 高级特性,如数据源扩展、查询优化器调优等,持续精进大数据处理技术栈。

相关推荐
Hacker_LaoYi44 分钟前
SQL注入的那些面试题总结
数据库·sql
Hacker_LaoYi3 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
独行soc3 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
Data跳动4 小时前
Spark内存都消耗在哪里了?
大数据·分布式·spark
独行soc4 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
woshiabc1115 小时前
windows安装Elasticsearch及增删改查操作
大数据·elasticsearch·搜索引擎
lucky_syq5 小时前
Saprk和Flink的区别
大数据·flink
lucky_syq5 小时前
流式处理,为什么Flink比Spark Streaming好?
大数据·flink·spark
袋鼠云数栈5 小时前
深入浅出Flink CEP丨如何通过Flink SQL作业动态更新Flink CEP作业
大数据
清平乐的技术专栏5 小时前
Hive SQL 查询所有函数
hive·hadoop·sql