1.简单来说,Hive是Shark的前身,Shark则是SparkSQL的前身,SparkSQL产生的原因则是由于Shark对于Hive的太多依赖(如采用Hive的语法解析器、查询优化器等等),制约了Spark的One Stack Rule Them All的既定方针,制约了Spark各个组件的相互集成。SparkSQL抛弃了原有Shark的代码,汲取了Shark的一些优点,如内存列存储(In-Memory Columnar Storage)、Hive兼容性等,重新开发了SparkSQL代码;由于摆脱了对Hive的依赖性,SparkSQL无论在数据兼容、性能优化、组件扩展方面都得到了极大的方便。
SparkSQL支持查询原生的RDD。 RDD是Spark平台的核心概念,是Spark能够高效的处理大数据的各种场景的基础。
2. SparkSQL作用:提供一个编程抽象(DataFrame) 并且作为分布式 SQL 查询引擎。
3.SparkSQL运行原理简述:将 Spark SQL 转化为 RDD, 然后提交到集群执行。
4.SparkSQL特点:
4.1 易整合:将sql查询与spark程序无缝混合,可以使用java、scala、python、R等语言的API操作。
4.2 统一的数据访问方式:可以用相同的方式连接到任何数据源。
4.3 兼容Hive
4.4 标准的数据连接
5.SparkSession:
SparkSession是Spark 2.0引如的新概念。SparkSession为用户提供了统一的切入点,来让用户学习spark的各项功能。
在spark的早期版本中,SparkContext是spark的主要切入点,由于RDD是主要的API,我们通过sparkcontext来创建和操作RDD。对于每个其他的API,我们需要使用不同的context。例如,对于Streming,我们需要使用StreamingContext;对于sql,使用sqlContext;对于Hive,使用hiveContext。但是随着Dataset和DataFrame的API逐渐成为标准的API,就需要为他们建立接入点。所以在spark2.0中,引入SparkSession作为Dataset和DataFrame API的切入点,SparkSession封装了SparkConf、SparkContext和SQLContext。为了向后兼容,SQLContext和HiveContext也被保存下来。
SparkSession实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext),所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了sparkContext,所以计算实际上是由sparkContext完成的。
特点:
---- 为用户提供一个统一的切入点使用Spark 各项功能
---- 允许用户通过它调用 DataFrame 和 Dataset 相关 API 来编写程序
---- 减少了用户需要了解的一些概念,可以很容易的与 Spark 进行交互
---- 与 Spark 交互之时不需要显示的创建 SparkConf, SparkContext 以及 SQlContext,这些对象已经封闭在 SparkSession 中
6.Dataset与DataFrame:
Dataset也是一个分布式数据容器。与RDD类似,然而Dataset更像传统数据库的二维表格,除了数据以外,还掌握数据的结构信息,即schema。同时,与Hive类似,Dataset也支持嵌套数据类型(struct、array和map)。从API易用性的角度上 看, Dataset API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。
Dataset的底层封装的是RDD,当RDD的泛型是Row类型的时候,我们也可以称它为DataFrame。即Dataset
SparkSQL的数据源可以是JSON类型的字符串,JDBC,Parquent,Hive,HDFS等。
1.读取json格式的文件创建Dataset
代码:
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.appName("jsonFile")
.master("local")
.getOrCreate();
Dataset ds = sparkSession.read().format("json").load("json");
ds.show();
}
json文件:
{"name":"zhangsan","age": 18}
{"name":"lisi"}
{"name":"wangwu","age":18}
{"name":"laoliu","age":28}
{"name":"zhangsan","age":20}
{"name":"lisi"}
{"name":"wangwu","age":18}
{"name":"laoliu","age":28}
{"name":"zhangsan","age":28}
{"name":"lisi"}
{"name":"wangwu","age":18}
输出结果:
注意点:
Dataset ds = sparkSession.read().format("json").load("data/json");
// Dataset ds = sqlContext.read().json("data/json");
/**
* Displays the top 20 rows of Dataset in a tabular form. Strings more than 20 characters
* will be truncated, and all cells will be aligned right.
*
* @group action
* @since 1.6.0
*/
def show(): Unit = show(20)
2.通过json格式的RDD创建Dataset
代码:
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.appName("jsonRDD")
.master("local")
.getOrCreate();
/**
* 注意:
* 1.由于是java版,故通过javaSparkcontext.parallelize来创建json格式的JavaRDD
* 故我们通过sparkcontext来创建javaSparkcontext
* 2.如果是scala版,直接通过sparkcontext.parallelize来创建,就无需创建javaSparkcontext
*/
SparkContext sc = sparkSession.sparkContext();
JavaSparkContext jsc = new JavaSparkContext(sc);
JavaRDD nameRDD = jsc.parallelize(Arrays.asList(
"{'name':'jujingyi','age':\"18\"}",
"{\"name\":\"zhuxudan\",\"age\":\"19\"}",
"{\"name\":\"zhouzhiruo\",\"age\":\"20\"}"
));
JavaRDD scoreRDD = jsc.parallelize(Arrays.asList(
"{\"name\":\"jujingyi\",\"score\":\"100\"}",
"{\"name\":\"zhuxudan\",\"score\":\"200\"}",
"{\"name\":\"zhouzhiruo\",\"score\":\"300\"}"
));
Dataset nameds = sparkSession.read().json(nameRDD);
Dataset scoreds = sparkSession.read().json(scoreRDD);
//注册为临时表使用
nameds.createOrReplaceTempView("nameds");
scoreds.createOrReplaceTempView("scoreds");
Dataset ds =
sparkSession
.sql("select n.name,n.age,s.score from nameds n join scoreds s where n.name" +
"= s.name");
ds.show();
sc.stop();
}
输出结果:
3.非json格式的RDD创建Dataset
3.1 通过反射的方式将非json格式的RDD转换成Dataset
代码:
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.appName("reflect")
.master("local")
.getOrCreate();
SparkContext sc = sparkSession.sparkContext();
JavaSparkContext jsc = new JavaSparkContext(sc);
JavaRDD lineRDD = jsc.textFile("person.txt");
JavaRDD map = lineRDD.map(new Function() {
private static final long serialVersionUID = 1L;
@Override
public Person call(String v1) throws Exception {
Person person = new Person();
person.setId(v1.split(",")[0]);
person.setName(v1.split(",")[1]);
person.setAge(Integer.valueOf(v1.split(",")[2]));
return person;
}
});
/**
* 传入进去Person.class的时候,sqlContext是通过反射的方式创建Dataset
* 在底层通过反射的方式获得Person的所有field,结合RDD本身,生成Dataset
*/
Dataset dataFrame = sparkSession.createDataFrame(map, Person.class);
dataFrame.show();
}
person.txt文件:
1,zhuxudan,18
2,zhaoming,19
3,zhouzhiruo,20
4,jujingyi,34
输出结果:
注意点:
3.2 动态创建Schema将非RDD文件转换为Dataset
代码:
public class CreateDSFromStruct {
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.master("local")
.appName("struct")
.getOrCreate();
SparkContext sc = sparkSession.sparkContext();
JavaSparkContext jsc = new JavaSparkContext(sc);
JavaRDD line = jsc.textFile("person.txt");
JavaRDD map = line.map(new Function() {
private static final long serialVersionUID = 1L;
@Override
public Row call(String s) throws Exception {
return RowFactory.create(
s.split(",")[0],
s.split(",")[1],
Integer.valueOf(s.split(",")[2])
);
}
});
/**
* 动态构建DataFrame中的元数据,一般来说这里的字段可以来源自字符串,也可以来源于外部数据库
*/
List asList = Arrays.asList(
DataTypes.createStructField("id", DataTypes.StringType, true),
DataTypes.createStructField("name", DataTypes.StringType, true),
DataTypes.createStructField("age", DataTypes.IntegerType, true)
);
StructType schema = DataTypes.createStructType(asList);
Dataset row = sparkSession.createDataFrame(map,schema);
row.show();
row.printSchema();
sc.stop();
}
}
文件同上:
输出结果:
4. 读取parquet文件创建Dataset:
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.appName("parquet")
.master("local")
.getOrCreate();
Dataset df = sparkSession.read().json("json");
// sparkSession.read().format("json").load("./spark/json");
df.show();
/**
* 将Dataset保存成parquet文件,
* SaveMode指定存储文件时的保存模式:
* Overwrite:覆盖
* Append:追加
* ErrorIfExists:如果存在就报错
* Ignore:如果存在就忽略
* 保存成parquet文件有以下两种方式:
*/
df.write().mode(SaveMode.Overwrite).format("parquet").save("data/parquet");
// df.write().mode(SaveMode.Overwrite).parquet("data/parquet");
/**
* 加载parquet文件成Dataset
* 加载parquet文件有以下两种方式:
*/
Dataset load = sparkSession.read().format("parquet").load("data/parquet");
//// load = sparkSession.read().parquet("data/parquet");
load.show();
sparkSession.stop();
}
注意点:
df.write().mode(SaveMode.Overwrite)format("parquet") .save("./sparksql/parquet"); df.write().mode(SaveMode.Overwrite).parquet("./sparksql/parquet"); |
Overwrite:覆盖
Append:追加
ErrorIfExists:如果存在就报错
Ignore:如果存在就忽略
5.读取jdbc中的数据创建Dataset(以mysql为例)
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.master("local")
.appName("mysql")
.getOrCreate();
/**
* 第一种方式读取MySql数据库表,加载为Dataset
*/
Map options = new HashMap();
options.put("url", "jdbc:mysql://192.168.235.101:3306/goldmantis");
options.put("driver", "com.mysql.jdbc.Driver");
options.put("user", "root");
options.put("password", "xxxxxxxx");
options.put("dbtable", "sys_staffs");
Dataset ds = sparkSession.read().format("jdbc").options(options).load();
// ds.show();
ds.createOrReplaceTempView("staff");
/**
* 第二种方式读取MySql数据表加载为Dataset
*/
DataFrameReader reader = sparkSession.read().format("jdbc");
reader.option("url", "jdbc:mysql://192.168.235.101:3306/goldmantis");
reader.option("driver", "com.mysql.jdbc.Driver");
reader.option("user", "root");
reader.option("password", "xxxxxxxx");
reader.option("dbtable", "site_receiving");
Dataset receive = reader.load();
receive.createOrReplaceTempView("receive");
Dataset result = sparkSession.sql("select s.NAME,s.ACCOUNT_NO,s.ORG_NAME,r.flow_no,r.project_name " +
",r.remark from site_receiving r, sys_staffs s where s.ACCOUNT_NO = r.account_no ");
result.show();
result.registerTempTable("result");
Dataset row = sparkSession.sql("select * from result");
row.show();
}
6.读取hive中数据加载成Dataset;