(1)在Hadoop中运行SQL的工具
在Hadoop中运行SQL的工具有Hive、Impala、Apache Drill、Presto、Spark SQL等。
(2)Shark的发展历史
在三四年前,Hive可以说是SQL on Hadoop的唯一选择,负责将SQL编译成可扩展的MapReduce作业。鉴于Hive的性能以及与Spark的兼容,Shark项目由此而生。
Shark即Hive on Spark,本质上是通过Hive的HQL解析,把HQL翻译成Spark上的RDD操作,然后通过Hive的metadata获取数据库里的表信息,实际HDFS上的数据和文件,会由Shark获取并放到Spark上运算。
Shark的最大特性就是快和与Hive的完全兼容,且可以在shell模式下使用rdd2sql()这样的API,把HQL得到的结果集,继续在scala环境下运算,支持自己编写简单的机器学习或简单分析处理函数,对HQL结果进一步分析计算。
Shark项目源代码:https://github.com/amplab/shark。
在2014年7月1日的Spark Summit上,Databricks宣布终止对Shark的开发,将重点放到Spark SQL上。
Databricks表示,Spark SQL将涵盖Shark的所有特性,用户可以从Shark 0.9进行无缝的升级。
Databricks推广的Shark相关项目一共有两个,分别是Spark SQL和新的Hive on Spark(HIVE-7292)。
Databricks表示,Shark更多是对Hive的改造,替换了Hive的物理执行引擎,因此会有一个很快的速度。然而,不容忽视的是,Shark继承了大量的Hive代码,因此给优化和维护带来了大量的麻烦。
(3)Spark SQL发展的时间线
(4)Spark SQL的特点
1)Spark SQL运行SQL或HiveQL查询使用UDF,UDAF和SerDes(序列化反序列化)函数。
2)通过JDBC或ODBC将Tableau等连接到Spark SQL。
3)使用Python、Scala、Java和R语言开发。
编译Spark时指定支持Hive:(Apache Hadoop 2.4.x支持Hive 1.3)
mvn -Pyarn -Phadoop-2.4 -Dhadoop.version=2.4.0 -Phive -Phive-thriftserver -DskipTests clean package
(1)将hive的hive-site.xml文件复制或者软链接到spark的conf文件夹中。
~]$ cd /opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/conf
conf]$ ln -s /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/conf/hive-site.xml
conf]$ ll
lrwxrwxrwx 1 beifeng beifeng 54 Jul 28 18:31 hive-site.xml -> /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/conf/hive-site.xml
(2)/opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/conf/hive-site.xml文件内容如下。
javax.jdo.option.ConnectionURL
jdbc:mysql://bigdata-senior.ibeifeng.com:3306/metadata?createDatabaseIfNotExist=true
javax.jdo.option.ConnectionDriverName
com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName
root
javax.jdo.option.ConnectionPassword
123456
hive.metastore.warehouse.dir
/user/hive/warehouse
hive.exec.mode.local.auto
true
hive.exec.mode.local.auto.input.files.max
100
hive.exec.mode.local.auto.inputbytes.max
13421772800000
hive.cli.print.header
true
hive.cli.print.current.db
true
hive.server2.thrift.port
10000
hive.server2.thrift.bind.host
bigdata-senior.ibeifeng.com
hive.metastore.uris
thrift://bigdata-senior.ibeifeng.com:9083
(3)根据hive的配置文件的内容选择不同的操作方式,这里指根据hive.metastore.uris参数的配置值来选择不同的操作方式,值默认为空。
1)如果没有给定参数(默认情况):
将hive元数据数据库的驱动包添加到spark的classpath环境变量中即可完成spark和hive的集成。
2)给定具体的metastore服务所在的节点信息(值非空)
启动hive的metastore服务:
cd /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6
hive-0.13.1-cdh5.3.6]$ bin/hive --service metastore &
[1] 5146
[beifeng@bigdata-senior hive-0.13.1-cdh5.3.6]$ Starting Hive Metastore Server
[beifeng@bigdata-senior hive-0.13.1-cdh5.3.6]$ jps -ml
5146 org.apache.hadoop.util.RunJar /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/hive-service-0.13.1-cdh5.3.6.jar org.apache.hadoop.hive.metastore.HiveMetaStore
5225 sun.tools.jps.Jps -ml
[beifeng@bigdata-senior hive-0.13.1-cdh5.3.6]$ jps
5146 RunJar
5235 Jps
(4)SparkSQL和Hive集成测试
1)启动NameNode,DataNode
2)启动SparkSQL
cd /opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/spark-sql
spark-sql (default)> select * from common.emp;
spark-sql (default)> select * from common.emp a join common.dept b on a.deptno = b.deptno;
spark-sql (default)> explain select * from common.emp a join common.dept b on a.deptno = b.deptno;
3)启动Spark Shell
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/spark-shell
scala> sqlContext.sql("select * from common.emp a join common.dept b on a.deptno = b.deptno").show()
4)查看SparkSQL和Spark Shell界面
SparkSQL: http://bigdata-senior.ibeifeng.com:4040/jobs/
Spark Shell: http://bigdata-senior.ibeifeng.com:4041/jobs/
(5)Spark应用依赖第三方jar文件解决方案
1)使用参数–jars添加本地的第三方jar文件,可以给定多个,使用逗号分隔。注意:要求jar文件在driver和client的机器上存在,适合依赖比较少的情况,即jar文件在本地存在。
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/spark-shell --jars /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/mysql-connector-java-5.1.27-bin.jar,/opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/derby-10.10.1.1.jar
在Spark Shell界面:http://bigdata-senior.ibeifeng.com:4041/jobs/ 查看添加的jar文件。
2)使用参数–packages添加maven中央库中的第三方jar文件,可以给定多个,使用逗号分隔。注意:下载的jar文件会保存到当前用户的根目录下的.ivy2文件夹的jars文件夹中,eg:/home/beifeng/.ivy2/jars;适合依赖比较少的情况。jar文件从maven源下载。
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/spark-shell --packages mysql:mysql-connector-java:5.1.27
3)使用SPARK_CLASSPATH环境变量给定jar文件的位置信息。注意:要求所有可以执行的节点都需要进行该配置,如果是spark on yarn,要求所有机器上有对应文件夹的jar文件。
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ mkdir -p external_jars
即该位置:/opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/external_jars
SPARK_CLASSPATH=/opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/external_jars/*
external_jars]$ cp /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/mysql-connector-java-5.1.27-bin.jar .
external_jars]$ cp /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/derby-10.10.1.1.jar .
启动Spark Shell
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/spark-shell
http://bigdata-senior.ibeifeng.com:4042/environment/
jar文件明确给定路径信息:
4)将依赖的jar文件打包到spark应用的jar文件中。注意:只适合jar文件比较小,而且应用依赖的jar文件不多的情况。
5)Spark on Yarn Cluster 第三方jar文件驱动解决方案(推荐)
将第三方的jar文件copy到${HADOOP_HOME}/share/hadoop/common/lib
文件夹中或者${HADOOP_HOME}/share/hadoop/yarn/lib
文件夹中。
在SparkSQL源码中,SQLContext是SparkSQL的入口,依赖于SparkContext。
HiveContext:当SparkSQL和Hive集成的时候(也就是SparkSQL可以访问Hive的元数据),必须使用HiveContext作为SparkSQL的入口。
DataFrame:SparkSQL中的核心抽象,类似于RDD,都是分布式数据集。
(1)SparkSQL的ThriftServer服务简述
SparkSQL的ThriftServer服务其实就是Hive的HiveServer2服务,只是将底层的执行改成Spark,同时在Spark上启动。
SparkSQL的ThriftServer服务官方帮助文档:
https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2
http://spark.apache.org/docs/1.6.1/sql-programming-guide.html#distributed-sql-engine
(2)配置SparkSQL的ThriftServer服务
1)在hive-site.xml中修改Hiveserver2的配置信息,如端口号和监听的IP地址:
hive.server2.thrift.port=10000
hive.server2.thrift.host=0.0.0.0
2)启动ThriftServer服务
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ sbin/start-thriftserver.sh
查看启动进程:
[beifeng@bigdata-senior spark-1.6.1-bin-2.5.0-cdh5.3.6]$ jps -ml
5146 org.apache.hadoop.util.RunJar /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/hive-service-0.13.1-cdh5.3.6.jar org.apache.hadoop.hive.metastore.HiveMetaStore
8701 sun.tools.jps.Jps -ml
5616 org.apache.spark.deploy.SparkSubmit --class org.apache.spark.sql.hive.thriftserver.SparkSQLCLIDriver spark-internal
5544 org.apache.hadoop.hdfs.server.datanode.DataNode
6668 org.apache.spark.deploy.SparkSubmit --class org.apache.spark.repl.Main --name Spark shell --jars /opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/mysql-connector-java-5.1.27-bin.jar,/opt/cdh-5.3.6/hive-0.13.1-cdh5.3.6/lib/derby-10.10.1.1.jar spark-shell
8555 org.apache.spark.deploy.SparkSubmit --class org.apache.spark.sql.hive.thriftserver.HiveThriftServer2 spark-internal
5457 org.apache.hadoop.hdfs.server.namenode.NameNode
注意:ThriftServer服务也是一个Spark的应用,所以可以给定参数,也可以进行优化(主要是资源调优)
3)停止ThriftServer服务
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ sbin/stop-thriftserver.sh
(3)SparkSQL的ThriftServer服务测试
1)通过命令beeline来测试
spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/beeline
Beeline version 1.6.1 by Apache Hive
beeline> !connect jdbc:hive2://bigdata-senior.ibeifeng.com:10000
Connecting to jdbc:hive2://bigdata-senior.ibeifeng.com:10000
Enter username for jdbc:hive2://bigdata-senior.ibeifeng.com:10000: beifeng
Enter password for jdbc:hive2://bigdata-senior.ibeifeng.com:10000: *******
19/07/28 23:15:29 INFO jdbc.Utils: Supplied authorities: bigdata-senior.ibeifeng.com:10000
19/07/28 23:15:29 INFO jdbc.Utils: Resolved authority: bigdata-senior.ibeifeng.com:10000
19/07/28 23:15:29 INFO jdbc.HiveConnection: Will try to open client transport with JDBC Uri: jdbc:hive2://bigdata-senior.ibeifeng.com:10000
Connected to: Spark SQL (version 1.6.1)
Driver: Spark Project Core (version 1.6.1)
Transaction isolation: TRANSACTION_REPEATABLE_READ
在beeline命令行输入SQL语句:
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1>
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1> show databases;
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1> use default;
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1> show tables;
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1> select * from default.emp a join default.dept b on a.deptno = b.deptno;
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1> !help # 查看帮助信息
0: jdbc:hive2://bigdata-senior.ibeifeng.com:1> !quit
Closing: 0: jdbc:hive2://bigdata-senior.ibeifeng.com:10000
2)编码测试,通过JDBC连接SparkSQL提供的ThriftServer服务
添加hive-jdbc驱动的pom依赖。这个依赖最好使用aliyun的maven源进行下载,需要检查一下是否已配置该maven源:http://maven.aliyun.com/nexus/content/groups/public/。
maven源配置如下:
aliyun
http://maven.aliyun.com/nexus/content/groups/public/
cloudera
https://repository.cloudera.com/artifactory/cloudera-repos
pom依赖坐标如下:
org.spark-project.hive
hive-jdbc
0.13.1
注意:给定用户名的时候,如果HDFS没有做权限验证,可以给定任何值;如果做了权限验证,必须给定操作tmp文件夹有权限的用户名称。
1)将测试文件上传HDFS上的/beifeng/spark/sql/data目录。
hadoop-2.5.0-cdh5.3.6]$ pwd
/opt/cdh-5.3.6/hadoop-2.5.0-cdh5.3.6
hadoop-2.5.0-cdh5.3.6]$ bin/hdfs dfs -mkdir -p /user/beifeng/spark/sql/data
hadoop-2.5.0-cdh5.3.6]$ cd /opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/examples/src/main/resources
resources]$ /opt/cdh-5.3.6/hadoop-2.5.0-cdh5.3.6/bin/hdfs dfs -put ./* /user/beifeng/spark/sql/data
2)编写SparkSQL代码
启动Hive Metastore:hive-0.13.1-cdh5.3.6]$ bin/hive --service metastore
启动Spark-Shell:spark-1.6.1-bin-2.5.0-cdh5.3.6]$ bin/spark-shell
scala> val path = "/user/beifeng/spark/sql/data/people.json"
path: String = /user/beifeng/spark/sql/data/people.json
scala> val df = sqlContext.jsonFile(path)
scala> df.show()
+----+-------+
| age| name|
+----+-------+
|null|Michael|
| 30| Andy|
| 19| Justin|
+----+-------+
scala> df.registerTempTable("json_people")
scala> sqlContext.sql("show tables").show
+-----------+-----------+
| tableName|isTemporary|
+-----------+-----------+
|json_people| true|
+-----------+-----------+
scala> sqlContext.dropTempTable("json_people")
scala> sqlContext.sql("show tables").show
+---------+-----------+
|tableName|isTemporary|
+---------+-----------+
+---------+-----------+
scala> df.registerTempTable("json_people")
scala> sqlContext.sql("show tables").show
+-----------+-----------+
| tableName|isTemporary|
+-----------+-----------+
|json_people| true|
+-----------+-----------+
scala> sqlContext.tableNames()
res7: Array[String] = Array(json_people)
scala> sqlContext.sql("select age, name from json_people where age is not null").show()
+---+------+
|age| name|
+---+------+
| 30| Andy|
| 19|Justin|
+---+------+
scala> sqlContext.sql("select age, name from json.`/user/beifeng/spark/sql/data/people.json` where age is not null").show()
+---+------+
|age| name|
+---+------+
| 30| Andy|
| 19|Justin|
+---+------+
scala> df
res10: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> df.rdd
res11: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[38] at rdd at :30
scala> df.schema
res12: org.apache.spark.sql.types.StructType = StructType(StructField(age,LongType,true), StructField(name,StringType,true))
(1)DataFrame的概念
Spark1.3才出现DataFrame的概念。在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格。
DataFrame与RDD的主要区别在于,前者带有schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型。这使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。反观RDD,由于无从得知所存数据元素的具体内部结构,Spark Core只能在stage层面进行简单、通用的流水线优化。
DataFrame与RDD的对比如下:
(2)DataFrame的创建方式
val df = sqlContext.##
1)SparkSQL的操作:
2)执行结果输出:
val resultRDD = resultDataFrame.rdd.map(row => (row.getAs[Long](0),row.getAs[String](1)))
resultRDD.saveAsXXX
df.show()
df.##
3)SparkSQL应用的处理流程:
(3)DataFrame内部是一个逻辑计划
所有的数据执行都是懒加载的。调用相关的API,实质上实在内部构建一个查询的逻辑计划,类似RDD的构建过程;只有当DataFrame被触发调用(获取数据的这种操作)的时候,才会真正的执行。
job执行步骤:
逻辑计划 --> 分析逻辑计划 --> 优化逻辑计划 --> 物理计划产生 --> 选择一个最优的物理计划 --> SparkCore代码生成 --> job执行。
(4)RDD和DataFrame转换
1)DataFrame转换为RDD:直接调用DataFrame类提供的rdd方法即可将
DataFrame转换为RDD数据类型。
2)RDD转换为DataFrame:将RDD转换为DataFrame主要有两种方式,如下:
官方说明文档:
https://spark-packages.org/
https://github.com/databricks/
DataFrame的read和write编程模式是通过SparkSQL内部定义的read和write数据读写入口进行数据的加载和保存操作。
(1)读数据
val df = sqlContext.read.###.load()
def read: DataFrameReader = new DataFrameReader(this)
函数功能说明:
format:给定读取数据源的数据格式是什么
schema:给定数据的数据格式,如果不给定,会自动进行推断
option:给定读取数据需要的参数
load:加载数据形成DataFrame
jdbc:读取RDBMs数据库的数据形成DataFrame
(2)三个不同jdbc API的功能
1)给定url和表名称及user&password即可读取数据,内部形成的DataFrame的分区数是1个:
def jdbc(url: String, table: String, properties: Properties)
2)给定形成的DataFrame的分区数量以及进行数据分区的字段,要求分区字段的数据类型必须是数值类型的:
def jdbc(
url: String,
table: String,
columnName: String, // 给定分区字段的列名称
lowerBound: Long, // 给定计算范围下界
upperBound: Long, // 给定计算范围上界
numPartitions: Int, // 给定分区个数
connectionProperties: Properties): DataFrame
步长及索引如下计算:
step = (upperBound - lowerBound) / numPartitions
currentIndex = step + lowerBound ==> (负无穷大,currentIndex]
preIndex = currentIndex
currentIndex += step ===> (preIndex, currentIndex]
===> 直到分区数量为numPartitions - 1
给定上界的范围 ===> (currentIndex,正无穷大)
3)明确给定进行数据分区的字段条件
def jdbc(
url: String,
table: String,
predicates: Array[String], // 数据分区的字段条件,predicates集合中的数据个数就是最终的数据分区个数
connectionProperties: Properties): DataFrame
(3)写数据
df.write.###.save()
def write: DataFrameWriter = new DataFrameWriter(this)
函数功能说明:
mode:给定数据插入的策略(数据插入的文件夹或者表是否存在):
format:给定数据输出的格式。
option:给定参数。
partitionBy:给定分区字段。
save:将数据进行保存操作。
insertInto:将数据插入到一个表中。
saveAsTable: 将数据保存为一个表。
jdbc:将数据输出到关系型数据库中。
(4)读写数据示例
读数据示例:
df = sqlContext.read \
.format("json") \
.option("samplingRatio", "0.1") \
.load("/home/michael/data.json")
写数据示例:
df.write \
.format("parquet") \
.mode("append") \
.partitionBy("year") \
// .save("fasterData")
.saveAsTable("fasterData")
先读后写示例(使用传统数据源的ETL):
sqlContext.read \
.format("com.databricks.spark.git") \
.option("url", "https://github.com/apache/spark.git") \
.option("numPartitions", "100") \
.option("branches", "master, branch-1.3, branch-1.2") \
.load() \
.repartition(1) \
.write \
.format("json") \
.save("/home/michael/spark.json")
(5)不同方式计算平均数
1)SQL语句
SELECT name, avg(age) FROM people GROUP BY name
2)MapReduce计算模型
private IntWritable one = new IntWritable(1)
private IntWritable output = new IntWritable()
protected void map(LongWritable key, Text value, Context context) {
String[] fields = value.split("\t")
output.set(Integer.parseInt(fields[1]))
context.write(one, output)
}
IntWritable one = new IntWritable(1)
DoubleWritable average = new DoubleWritable()
protected void reduce(IntWritable key, Iterable values, Context context) {
int sum = 0
int count = 0
for(IntWritable value: values) {
sum += value.get()
count++
}
average.set(sum/(double)count)
context.write(key, average)
}
3)Spark Core(RDD)计算模型
data = sc.textFile(...).split("\t")
data.map(lambda x: (x[0], [int(x[1], 1])) \
.reduceByKey(lambda x, y: [x[0] + y[0], x[1] + y[1]]) \
.map(lambda x: [x[0], x[1][0] / x[1][1]]) \
.collect()
4)Spark SQL(DataFrame)计算模型
sqlContext.table("people") \
.groupBy("name") \
.agg("name", avg("age")) \
.collect()
(6)SQL语句转换成DataFrame
events = sqlContext.load("/data/events", "parquet")
training_data = events.where("city = 'New York' and year = 2015")
.select("timestamp")
.collect()
(7)每个Spark Application以loading data开始,以saving data结束。
需求:将Hive表数据输出到MySQL表中,将Hive表和MySQL表进行数据Join操作,并将最终结果保存为Parquet格式的数据,存储在HDFS中。
step1. 将spark-hive的依赖添加到项目的pom文件中。
org.apache.spark
spark-hive_2.10
${spark.version}
compile
mysql
mysql-connector-java
5.1.27
step2. Windows平台上执行过程中可能出现的异常:
Exception in thread “main” java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method)。
【解决方案】run->edit configurations中给定运行的JVM参数-XX:PermSize=128M -XX:MaxPermSize=256M。
由于hadoop在windows上和linux上的执行方式不一样,在使用hiveContext对象的时候,需要应用到hadoop的底层mapreduce的一些相关代码,如果环境和源码之间存在着兼容问题的话,有可能出现NullPointException异常。
【解决方案】直接修改hadoop的底层源码,然后将修改好的源码放到spark项目中。
如果配置了HADOOP_USER_NAME。
【解决方案】建议删除(windows中)。
17/05/21 15:02:44 ERROR ShutdownHookManager: Exception while deleting Spark temp dir: C:\Users\ibf\AppData\Local\Temp\spark-d1d77acb-0a02-4db8-b2ba-217796d96207
java.io.IOException: Failed to delete: C:\Users\ibf\AppData\Local\Temp\spark-d1d77acb-0a02-4db8-b2ba-217796d96207。
【解决方案】不解决,删除临时文件失败导致的,不会影响正式的业务代码,只会在windows上产生。
(1)窗口分析函数
Hive支持的内置函数,SparkSQL基本上都支持,需要稍微注意一下的是:有一些Hive的函数的使用需要使用HiveContext对象,不能使用SQLContext对象来操作。如对于Hive中的窗口分析函数,必须使用HiveContext作为入口。如下例:
row_number()的窗口分析函数用来解决分组排序TopN的问题。
scala> sqlContext.sql("select deptno, sal, row_number() over (partition by deptno order by sal desc) as rnk from common.emp").show
+------+------+---+
|deptno| sal|rnk|
+------+------+---+
| 10|5000.0| 1|
| 10|2450.0| 2|
| 10|1300.0| 3|
| 20|3000.0| 1|
| 20|3000.0| 2|
| 20|2975.0| 3|
| 20|1100.0| 4|
| 20| 800.0| 5|
| 30|2850.0| 1|
| 30|1600.0| 2|
| 30|1500.0| 3|
| 30|1250.0| 4|
| 30|1250.0| 5|
| 30| 950.0| 6|
+------+------+---+
(2)自定义函数
SparkSQL支持两种自定义函数,分别是:UDF和UDAF,两种函数都是通过SQLContext的udf属性进行函数的注册使用的;SparkSQL不支持UDTF函数的自定义使用。
UDF:一条数据输入,一条数据输出,一对一的函数,即普通函数。
UDAF:多条数据输入,一条数据输出,多对一的函数,即聚合函数。
(1)DataSet的创建
官方文档:http://spark.apache.org/docs/1.6.1/sql-programming-guide.html#creating-datasets
scala> import sqlContext.implicits._
import sqlContext.implicits._
scala> val ds = Seq(1,2,3).toDS()
20/01/27 01:41:33 INFO codegen.GenerateUnsafeProjection: Code generated in 153.240623 ms
ds: org.apache.spark.sql.Dataset[Int] = [value: int]
scala> ds.show()
+-----+
|value|
+-----+
| 1|
| 2|
| 3|
+-----+
scala> val df = sqlContext.read.json("/user/beifeng/spark/sql/data/people.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> df
res1: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> case class Person(age: Long, name: String)
defined class Person
scala> df.as[Person]
res2: org.apache.spark.sql.Dataset[Person] = [age: bigint, name: string]
(2) DataSet,DataFrame与RDD的比较
1)相同点
三者都是分布式数据集。
DataSet和DataFrame的相同点都是有数据特征、数据类型的分布式数据集(Schema)。而RDD没有Schema。
2)不同点
RDD中的数据是没有数据类型的;
DataFrame中的数据是弱数据类型,不会做数据类型检查;
DataSet中的数据类型是强数据类型。
SparkSQL除了支持直接的HQL语句的查询外,还支持通过DSL语句/API进行数据的操作,主要DataFrame API列表如下:
SparkCore中的优化项一般都需要考虑,比如cache、重用等。
SparkSQL专有的优化项: