一、什么是Spark SQL?特点、数据模型DataFrame
1、是Spark的一个模块,用于处理结构化数据
spark Sql 就是把spark-core中rdd的数据进行结构化,使之能用sql语句的方式进行处理这些数据。
2、特点
- 容易集成,已经被集成到了Spark中
- 提供统一的数据访问方式:Oracle、MySQL、JSON、CSV等等 —> 数据模型DataFrame
- 兼容Hive:在实际工作中,用得很少
- 支持标准的JDBC、ODBC
3、数据模型DataFrame -----> 表
表(DataFrame) = 表结构(Schema) + 数据(RDD)
Schema可以用case class表示(样本类),还有其他方式
DataFrame从本质上,就是一个RDD,表现形式就是RDD
4、Demo:创建DataFrame:数据员工表emp.csv
Java版:
添加依赖
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>2.3.2</version>
</dependency>
</dependencies>
(1)使用case class
既然spark SQL就是对rdd中的数据进行结构化,那我们是不是可以提前把数据进行规范,用对象将数据进行规范,最后在把转化为sparksql。
public class SparkSqlCase {
public static void main(String[] args) {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR);
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkJavaSql");
JavaSparkContext sc = new JavaSparkContext(conf);
// 从这里可以看出,sparksql是作用在spark core上面。
// 就是对rdd进一步的封装。
SQLContext sqlc = new SQLContext(sc);
// 既然spark SQL就是对rdd中的数据进行结构化,那我们是不是可以提前把数据进行
// 规范,用对象将数据进行规范,最后在把转化为sparksql
JavaRDD<Emp> map = sc.textFile("sparkSql/src/main/resources/emp.csv").map(new Function<String, Emp>() {
@Override
public Emp call(String s) throws Exception {
// "empno":7369,"ename":"SMITH","job":"CLERK","mgr":"7902","hiredate":"1980/12/17"
// ,"sal":800,"comm":"","deptno":20
String[] split = s.split(",");
Emp emp = new Emp();
emp.setEmpno(Integer.parseInt(split[0]));
emp.setEname(split[1]);
emp.setJob(split[2]);
emp.setMgr(Integer.parseInt(split[3]));
emp.setHiredate(split[4]);
emp.setSal(Integer.parseInt(split[5]));
emp.setComm(split[6]);
emp.setDeptno(Integer.parseInt(split[7]));
return emp;
}
});
Dataset<Row> dataFrame = sqlc.createDataFrame(map, Emp.class);
dataFrame.createOrReplaceTempView("emp");
sqlc.sql("select * from emp").show();
sc.stop();
}
}
(2)使用StructType
注意下面代码的编写思路,
public class SparkJavaSql {
public static void main(String[] args) {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR);
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkJavaSql");
JavaSparkContext sc = new JavaSparkContext(conf);
// 从这里可以看出,sparksql是作用在spark core上面。
// 就是对rdd进一步的封装。
SQLContext sqlc = new SQLContext(sc);
// "empno":7369,"ename":"SMITH","job":"CLERK","mgr":"7902","hiredate":"1980/12/17"
//,"sal":800,"comm":"","deptno":20
// 首先用spark core读出数据,并定义row行数据
JavaRDD<Row> rowrdd = sc.textFile("sparkSql/src/main/resources/emp.csv").map(new Function<String, Row>() {
@Override
public Row call(String s) throws Exception {
String[] split = s.split(",");
// 通过工厂模式进行构造row
return RowFactory.create(Integer.parseInt(split[0]),split[1],split[2],Integer.parseInt(split[3]),split[4],
Integer.parseInt(split[5]),split[6],Integer.parseInt(split[7]));
}
});
// 构建StructType,用于最后DataFrame元数据的描述
List<StructField> listfields = new ArrayList<StructField>();
listfields.add(DataTypes.createStructField("empno", DataTypes.IntegerType, false));
listfields.add(DataTypes.createStructField("ename", DataTypes.StringType, false));
listfields.add(DataTypes.createStructField("job", DataTypes.StringType, false));
listfields.add(DataTypes.createStructField("mgr", DataTypes.IntegerType, false));
listfields.add(DataTypes.createStructField("hiredate", DataTypes.StringType, false));
listfields.add(DataTypes.createStructField("sal", DataTypes.IntegerType, false));
listfields.add(DataTypes.createStructField("comm", DataTypes.StringType, true));
listfields.add(DataTypes.createStructField("deptno", DataTypes.IntegerType, false));
StructType structType = DataTypes.createStructType(listfields);
// 将spark core读出的数据进行封装为spark sql
// 主要分为两部分,定义行数据,定义列的类型。
Dataset<Row> dataFrame = sqlc.createDataFrame(rowrdd, structType);
dataFrame.createOrReplaceTempView("emp");
sqlc.sql("select * from emp").show();
sc.stop();
}
}
(3)使用SparkSession.read.load函数-----> 最简单
上面的两种都是把rdd的数据进行转换,而sparksql也可以直接读取文件,直接转化为sparksql。
public class sparkSqlLoad {
public static void main(String[] args) {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR);
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkJavaSql");
JavaSparkContext sc = new JavaSparkContext(conf);
// 从这里可以看出,sparksql是作用在spark core上面。
// 就是对rdd进一步的封装。
SQLContext sqlc = new SQLContext(sc);
Dataset<Row> jsondata = sqlc.read().json("sparkSql/src/main/resources/emp.json");
jsondata.createOrReplaceTempView("emp");
sqlc.sql("select * from emp").write()
.format("json").save("sparkSql/src/main/resources/emp1");
sc.stop();
}
}
连接Oracle数据库
5、视图
(1)局部视图:只能在当前会话中使用
df.createOrReplaceTempView("emp1");
spark.sql("select * from emp1 order by sal").show();
(2)全局视图:可以在不同的会话中使用
df.createOrReplaceGlobalTempView("emp2");
注意:全局视图是一个命名空间的global_temp
spark.sql("select * from global_temp.emp2 order by sal").show()
在一个新会话中,执行查询
spark.newSession.sql("select * from emp1 order by sal").show ---> 错误
spark.newSession.sql("select * from global_temp.emp2 order by sal").show
二、Spark SQL的数据源
使用的就是spark sql直接读取文件。
1、Parquet文件:是Spark SQL默认的数据源,是一种列式存储的文件
支持表(DataFrame)的合并
还可以把其他格式的数据,转换成是Parquet文件
2、数据源JSON、CSV与在上面也都介绍过
3、数据源也可以是jdbc,进行连接Oracle、MySQL数据库
- 将数据加载到Oracle上面。
public class SparkSqlOjdbc {
public static void main(String[] args) throws Exception {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR);
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkJavaSql");
JavaSparkContext sc = new JavaSparkContext(conf);
// 从这里可以看出,sparksql是作用在spark core上面。
// 就是对rdd进一步的封装。
SQLContext sqlc = new SQLContext(sc);
Dataset<Row> jsondata = sqlc.read().json("sparkSql/src/main/resources/emp.json");
jsondata.createOrReplaceTempView("emp");
Properties oracleprops = new Properties();
oracleprops.setProperty("user","scott");
oracleprops.setProperty("password","scott");
oracleprops.setProperty("driver","oracle.jdbc.driver.OracleDriver");
Dataset<Row> sql = sqlc.sql("select * from emp");
sql.write().mode(SaveMode.Overwrite).jdbc("jdbc:oracle:thin:@192.168.92.135:1521/orcl"
,"scott.emp",oracleprops);
sc.stop();
}
}
- 从Oracle上面加载数据
public class OracleInput {
public static void main(String[] args) {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR);
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkJavaSql");
JavaSparkContext sc = new JavaSparkContext(conf);
// 从这里可以看出,sparksql是作用在spark core上面。
// 就是对rdd进一步的封装。
SQLContext sqlc = new SQLContext(sc);
Properties oracleprops = new Properties();
oracleprops.setProperty("user","scott");
oracleprops.setProperty("password","scott");
oracleprops.setProperty("driver","oracle.jdbc.driver.OracleDriver");
Dataset<Row> jdbc = sqlc.read().jdbc("jdbc:oracle:thin:@192.168.92.135:1521/orcl", "scott.emp",
oracleprops);
jdbc.show();
sc.stop();
}
}
方式二:将上面sqlc改成下面代码
Dataset<Row> jdbc = sqlc.read().format("jdbc")
.option("url", "jdbc:oracle:thin:@192.168.92.135:1521/orcl")
.option("dbtable", "scott.emp")
.option("user", "scott")
.option("password", "scott").load();
4、集成Hive
1、只需要将以下文件拷贝到$SPARK_HOME/conf的目录下,即可
$HIVE_HOME/conf/hive-site.xml
$HADOOP_CONF_DIR/core-site.xml
$HADOOP_CONF_DIR/hdfs-site.xml
2、重启Spark
3、启动Spark Shell,需要加载mysql的驱动
bin/spark-shell --master spark://bigdata111:7077 --jars /root/jars/mysql-connector-java-5.1.43-bin.jar
三、Spark SQL的优化
在性能调优这一块,我们主要做的是缓存数据,我们不在把数据注册成一个视图,而是注册成一张表registerTempTable(“tableName”),然后使用cacheTable(“tableName”),把这张表缓存下来,以便下次更快的调用数据。
1、从Oracle数据库中读取数据,生成DataFrame
上面已经提到,这里不再赘述
2、将DataFrame注册成表
df.registerTempTable("emp")
3、将表进行缓存
spark.sqlContext.cacheTable("emp")
4、清空缓存
spark.sqlContext.cacheTable("emp")
spark.sqlContext.clearCache
java demo code
public class OracleInput {
public static void main(String[] args) {
Logger.getLogger("org.apache.spark").setLevel(Level.ERROR);
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkJavaSql");
JavaSparkContext sc = new JavaSparkContext(conf);
// 从这里可以看出,sparksql是作用在spark core上面。
// 就是对rdd进一步的封装。
SQLContext sqlc = new SQLContext(sc);
Dataset<Row> jdbc = sqlc.read().format("jdbc")
.option("url", "jdbc:oracle:thin:@192.168.92.135:1521/orcl")
.option("dbtable", "scott.emp")
.option("user", "scott")
.option("password", "scott").load();
// 注册表
sqlc.registerDataFrameAsTable(jdbc,"emp");
// 缓存表
sqlc.cacheTable("emp");
// 清空缓存
sqlc.clearCache();
jdbc.show();
sc.stop();
}
}