spark SQL 概述
Spark SQL是Apache Spark的用于处理结构化数据的模块。强调的是"结构化数据",而非"SQL"
Spark SQL 提供的操作数据的方式
- SQL
- DataFrame API
- Dataset API
集成
将SQL查询与Spark程序无缝混合。
Spark SQL使您可以使用SQL或熟悉的DataFrame API在Spark程序中查询结构化数据。可在Java,Scala,Python和R中使用。
results = spark.sql("SELECT * FROM people")
names = results.map(lambda p: p.name)将函数应用于SQL查询的结果。
统一数据访问
以相同的方式连接到任何数据源。
DataFrame和SQL提供了一种访问各种数据源的通用方法,包括Hive,Avro,Parquet,ORC,JSON和JDBC。您甚至可以跨这些源连接数据。
spark.read.json("s3n://...").registerTempTable("json")
results = spark.sql(
"""SELECT *
FROM people
JOIN json ...""")
Hive整合
在现有仓库上运行SQL或HiveQL查询。
Spark SQL支持HiveQL语法以及Hive SerDes和UDF,从而使您可以访问现有的Hive仓库。
标准连接
通过JDBC或ODBC连接。
服务器模式为商业智能工具提供了行业标准的JDBC和ODBC连接。
spark SQL 架构
l core处理数据的输入输出,从不同的数据源获取数据(RDD、Parquet、json等),将查询结果输出成schemaRDD;
l catalyst处理查询语句的整个处理过程,包括解析、绑定、优化、物理计划等,说其是优化器,还不如说是查询引擎;
l hive对hive数据的处理
l hive-ThriftServer提供CLI和JDBC/ODBC接口
在这四个模块中,catalyst处于最核心的部分,其性能优劣将影响整体的性能。由于发展时间尚短,还有很多不足的地方,但其插件式的设计,为未来的发展留下了很大的空间。下面是catalyst的一个设计图:
其中虚线部分是以后版本要实现的功能,实线部分是已经实现的功能。从上图看,catalyst主要的实现组件有:
lsqlParse,完成sql语句的语法解析功能,目前只提供了一个简单的sql解析器;
lAnalyzer,主要完成绑定工作,将不同来源的Unresolved LogicalPlan和数据元数据(如hive metastore、Schema catalog)进行绑定,生成resolved LogicalPlan;
loptimizer对resolved LogicalPlan进行优化,生成optimized LogicalPlan;
l Planner将LogicalPlan转换成PhysicalPlan;
l CostModel,主要根据过去的性能统计数据,选择最佳的物理执行计划
这些组件的基本实现方法:
l 先将sql语句通过解析生成Tree,然后在不同阶段使用不同的Rule应用到Tree上,通过转换完成各个组件的功能。
l Analyzer使用Analysis Rules,配合数据元数据(如hive metastore、Schema catalog),完善Unresolved LogicalPlan的属性而转换成resolved LogicalPlan;
l optimizer使用Optimization Rules,对resolved LogicalPlan进行合并、列裁剪、过滤器下推等优化作业而转换成optimized LogicalPlan;
l Planner使用Planning Strategies,对optimized LogicalPlan
Datasets and DataFrames
DataFrames:开发单机版应用程序一样来开发分布式应用程序
Datasets是数据的分布式集合。数据集是Spark 1.6中添加的新接口,它具有RDD的优点(强类型输入,使用强大的lambda函数的能力)以及Spark SQL的优化执行引擎的优点。数据集可以被构造从JVM对象,然后使用功能性的转换(操作map
,flatMap
,filter
等等)。Dataset API在Scala和 Java中可用。Python不支持Dataset API。但是由于Python的动态特性,Dataset API的许多优点已经可用(即,您可以自然地通过名称访问行的字段 row.columnName
)。R的情况类似。
DataFrame是组织为命名列的数据集(以列(列名、列类型、列值)的形式构成分布式的数据集)。从概念上讲,它等效于关系数据库中的表或R / Python中的数据框,但是在后台进行了更丰富的优化。可以从多种来源构造DataFrame,例如:结构化数据文件,Hive中的表,外部数据库或现有RDD。DataFrame API在Scala,Java,Python和R中可用。在Scala和Java中,DataFrame由的数据集表示Row
。在Scala API中,DataFrame
只是类型别名Dataset[Row]
。而在Java API中,用户需要使用Dataset<Row>
来代表DataFrame
。
RDD和DF的区别
上图直观地体现了DataFrame和RDD的区别。
左侧的RDD[Person]虽然以Person为类型参数,但Spark框架本身不了解Person类的内部结构。而右侧的DataFrame却提供了详细的结构信息,使得Spark SQL可以清楚地知道该数据集中包含哪些列,每列的名称和类型各是什么。DataFrame多了数据的结构信息,即schema。
RDD是分布式的Java对象的集合。DataFrame是分布式的Row对象的集合。
DataFrame除了提供了比RDD更丰富的算子以外,更重要的特点是提升执行效率、减少数据读取以及执行计划的优化,比如filter下推、裁剪等。(DataFrame比RDD的执行效率要高一点,因为在大数据的处理中,RDD即使用mappartition或者foreachRDD都要消耗不少的core,但是DataFrame他可以进行sql操作,先过滤掉一部分数据,在RDD中是不好实现的。)
from pyspark.sql import SparkSession,Row
from pyspark.sql.types import LongType,StringType,StructField,StructType
if __name__=="__main__":
spark=SparkSession.builder.appName("spark0801").getOrCreate()
# df = spark.read.json("file:///opt/modules/spark-2.4.4-bin-2.6.0-cdh5.7.0/examples/src/main/resources/people.json")
# Displays the content of the DataFrame to stdout
# df.printSchema()
# df.select("name").show()
# df.select(df.name,df.age+1).show()
# df.filter(df.age>29).show()
# df.groupBy("age").count().show()
# df.createOrReplaceTempView("people")
sc = spark.sparkContext
df= sc.textFile("file:///opt/modules/spark-2.4.4-bin-2.6.0-cdh5.7.0/examples/src/main/resources/people.txt")
# lines=df.map(lambda x:x.split(",")).map(lambda p:Row(name=p[0],age=p[1].strip()))
# print(lines.collect())
# people=spark.createDataFrame(lines)
lines=df.map(lambda x:x.split(",")).map(lambda p:(p[0],int(p[1].strip())))
Struct_Fields=[StructField("name",StringType(),True),StructField("age",LongType(),True)]
st=StructType(Struct_Fields)
people=spark.createDataFrame(lines,st)
people.createOrReplaceTempView("people")
sql_people=spark.sql("select * from people where age=30")
sql_people.show()
print(sql_people.rdd.collect())
spark.stop()
# try:
# # df=spark.read.load("file:///opt/modules/spark-2.4.4-bin-2.6.0-cdh5.7.0/examples/src/main/resources/users.parquet")
# # df.select("name","favorite_numbers").show()
# # df=spark.read.load("file:///opt/modules/spark-2.4.4-bin-2.6.0-cdh5.7.0/examples/src/main/resources/people.json", format="json")
# # df.select("name", "age").write.save("file:///opt/datas/namesAndAges.parquet",format='parquet',mode="append")
# # spark.sql("select * from parquet.`file:///opt/datas/namesAndAges.parquet`").show()
# df = spark.read.load("file:///opt/modules/spark-2.4.4-bin-2.6.0-cdh5.7.0/examples/src/main/resources/people.csv",
# format="csv", sep=";", inferSchema="true", header="true")
# df.show()
# except Exception as e :
# print(f"出错了,{e}")
# finally:
# spark.stop()