**
1.SparkSQL*
1. SparkSQL介绍
Spark SQL产生的根本原因时其完全脱离了Hive的限制
SparkSQL支持查询原生的RDD。RDD时Spark平台的核心概念,是Spark能够高效的处理大数据的各种场景的基础
能够在Scala中写SQL。支持简单的SQL语法检查,能够在Scala中写Hive语句访问Hive数据,并将结果取回作为RDD使用
2. Sprak on Hive & Hive on Spark
Sprak on Hive:Hive只作为存储角色,Spark负责Sql解析优化,执行。
Hive on Spark: Hive即作为存储又负责SQL的解析优化,Spark负责执行。
3.Dataset&DataFrame
Dataset是一个分布式数据容器
底层封装的是RDD,当RDD的泛型是Row类型的时候,我们也可以称它为DataFrame。即Dataset=DataFrame
4.SparkSQL数据源
SparkSQL的数据源可以是JSON类型的字符串,JDBC,Parquent,Hive,HDFS等。
5. SparkSQL底层架构
拿到sql后解析一批未被解决的逻辑计划
经过分析得到分析后的逻辑计划
经过一批优化规则转换成一批最佳优化的逻辑计划
经过SparkPlanner的策略转化成一批物理计划
经过消费模型转换成一个个Spark任务执行
**
2. 创建DataSet的几种方式
**
1. 读取Json格式的文件创建DataSet
注意:
json格式中的json数据不能嵌套json格式数据
DataRDD是一个个Row类型的RDD
可以两种方式读取json格式文件
df.show()默认显示前20行数据
Dataset原生API可以操作Dataset(不方便)
注册成临时表时,表中的列默认按ascii顺序显示列
public static void main(String[] args) {
SparkSession sparkSession = SparkSession.builder().appName("jsonFile").master("local").getOrCreate();
Dataset<Row> json = sparkSession.read().format("json").load("data/json");
json.show();
json.show(4);
树形的显示Schema信息
json.printSchema();
select name,age+100 as addage from table
json.select(json.col("name"), json.col("age").plus(100).alias("addage")).show();
select name ,age from table where age >10
json.select(json.col("name"),json.col("age")).where(json.col("age").gt(19)).show();
select age,count(*)from table group by age
json.groupBy(json.col("age")).count().show();
json.createOrReplaceTempView("jsonSession");
Dataset<Row> sql = sparkSession.sql("select age ,count(*) as gg from jsonSession group by age");
sql.show();
sparkSession.stop();
2. 通过json格式的RDD创建DataSet
SparkSession sparkSession = SparkSession.builder()
.master("local").appName("jsonRDD").getOrCreate();
SparkContext sc = sparkSession.sparkContext();
JavaSparkContext jsc = new JavaSparkContext(sc);
JavaRDD nameRDD = jsc.parallelize(Arrays.asList(
"{'name':'zhangsan','age':\"18\"}",
"{\"name\":\"lisi\",\"age\":\"19\"}",
"{\"name\":\"wangwu\",\"age\":\"20\"}"
));
JavaRDD<String> scoreRDD = jsc.parallelize(Arrays.asList(
"{\"name\":\"zhangsan\",\"score\":\"100\"}",
"{\"name\":\"lisi\",\"score\":\"200\"}",
"{\"name\":\"wangwu\",\"score\":\"300\"}"
));
Dataset name = sparkSession.read().json(nameRDD);
Dataset<Row> score = sparkSession.read().json(scoreRDD);
name.createOrReplaceTempView("nameTable");
score.createOrReplaceTempView("scoreTable");
Dataset<Row> sql = sparkSession.sql("select nameTable.name,nameTable.age,scoreTable.score" +
" from nameTable join scoreTable" +
" on nameTable.name = scoreTable.name");
sql.show();
sc.stop();
3. 非json格式的RDD创建DataSet
- 通过反射的方式将json格式的RDD转换成DataSet
自定义类有可序列化
自定义类的访问级别时Public
RDD转换DataSet后会根据映射将字段按Assici码排序
将DataSet转换成RDD时获取字段的两种方式,一种是 getInt(0)下标获取,另一种是getAs(“列名”)获取
SparkSession sparkSession = SparkSession.builder().appName("reflect").master("local").getOrCreate();
SparkContext sc = sparkSession.sparkContext();
JavaSparkContext jsc = new JavaSparkContext(sc);
JavaRDD<String> lineRDD = jsc.textFile("data/person.txt");
JavaRDD<Person> map = lineRDD.map(new Function<String, Person>() {
private static final long serialVersionUID = 1L;
@Override
public Person call(String v1) throws Exception {
Person ps = new Person();
ps.setId(v1.split(",")[0]);
ps.setName(v1.split(",")[1]);
ps.setAge(Integer.valueOf(v1.split(",")[2]));
return ps;
}
});
Dataset<Row> dataFrame = sparkSession.createDataFrame(map, Person.class);
dataFrame.show();
dataFrame.printSchema();
dataFrame.registerTempTable("person");
Dataset<Row> sql = sparkSession.sql("select id,name,age from person where id > 2");
sql.show();
/*
* 将DataSet转为JavaRDD
*
* */
final JavaRDD<Row> rowJavaRDD = dataFrame.javaRDD();
JavaRDD<Person> map1 = rowJavaRDD.map(new Function<Row, Person>() {
private static final long serialVersionUID = 1L;
@Override
public Person call(Row v1) throws Exception {
Person ps = new Person();
// ps.setName(v1.getAs(“name”));
// ps.setAge(v1.getAs(“age”));
// ps.setId(v1.getAs(“id”));
ps.setId(v1.getString(2));
ps.setName(v1.getString(1));
ps.setAge(v1.getInt(0));
return ps;
}
});
map1.foreach(new VoidFunction<Person>() {
@Override
public void call(Person person) throws Exception {
System.out.println(person);
}
});
sparkSession.stop();
-
动态创建Schema将非json格式的RDD转换位DataSet
SparkSession sparkSession = SparkSession.builder() .master("local").appName("struct").getOrCreate(); SparkContext sc = sparkSession.sparkContext(); JavaSparkContext jsc = new JavaSparkContext(sc); JavaRDD<String> lineRDD = jsc.textFile("data/person.txt"); JavaRDD<Row> map = lineRDD.map(new Function<String, Row>() { private static final long serialVersionUID = 1L; @Override public Row call(String v1) throws Exception { Row row = RowFactory.create( v1.split(",")[0], v1.split(",")[1], Integer.valueOf(v1.split(",")[2]) ); return row; } }); List<StructField> structFields = Arrays.asList( DataTypes.createStructField("id", DataTypes.StringType, true), DataTypes.createStructField("name", DataTypes.StringType, true), DataTypes.createStructField("age", DataTypes.IntegerType, true) ); StructType schema = DataTypes.createStructType(structFields); Dataset<Row> dataFrame = sparkSession.createDataFrame(map, schema); dataFrame.printSchema(); dataFrame.show(); sparkSession.stop();
4.读取parquet文件创建DataSet
SaveMode指定文件保存时的模式:
Overwrite:覆盖
Append:追加
ErrorIfExists:如果存在就报错
Ignore:如果存在就忽略
SparkSession sparkSession = SparkSession.builder().appName("parquer").master("local").getOrCreate();
Dataset<Row> json = sparkSession.read().json("data/json");
json.show();
json.write().mode(SaveMode.Overwrite).format("parquet").save("data/parquet");
Dataset<Row> parquet = sparkSession.read().parquet("data/parquet");
parquet.show();
sparkSession.stop();
-
读取JDBC中的数据创建DataSet
SparkSession sparkSession = SparkSession.builder().master("local").appName("mysql").getOrCreate(); /* * 第一种方式读取MySql数据库表,加载为Dataset */ Map<String, String> options = new HashMap<String, String>(); options.put("url", "jdbc:mysql://127.0.0.1:3306/spark"); options.put("driver", "com.mysql.jdbc.Driver"); options.put("user", "root"); options.put("password", "root"); options.put("dbtable", "person"); Dataset<Row> person = sparkSession.read().format("jdbc").options(options).load(); person.show(); person.createOrReplaceTempView("person"); /* * 第二种方式 * * */ DataFrameReader reader = sparkSession.read().format("jdbc"); reader.option("url", "jdbc:mysql://127.0.0.1:3306/spark"); reader.option("driver", "com.mysql.jdbc.Driver"); reader.option("user", "root"); reader.option("password", "root"); reader.option("dbtable", "score"); Dataset<Row> score = reader.load(); score.show(); score.createOrReplaceTempView("score"); Dataset<Row> sql = sparkSession.sql("select person.id,person.name,person.age,score.score " + " from person,score" + " where person.name = score.name and score >50"); sql.show(); sql.createOrReplaceTempView("result"); Dataset<Row> sql1 = sparkSession.sql("select id,name,age,score from result where age <15"); sql1.show(); /* * 将Dataset结果保存到Mysql中 * */ Properties properties = new Properties(); properties.setProperty("user","root"); properties.setProperty("password","root"); sql.write().mode(SaveMode.Overwrite).jdbc("jdbc:mysql://127.0.0.1:3306/spark", "result12",properties); System.out.println("已加入数据库"); sparkSession.stop();
-
读取Hive中的数据加载成DataSet
SparkSession sparkSession = SparkSession.builder().appName("hive").enableHiveSupport().getOrCreate(); sparkSession.sql("USE spark"); sparkSession.sql("DROP TABLE IF EXISTS student_infos"); //在hive中创建student_infos表 sparkSession.sql("CREATE TABLE IF NOT EXISTS student_infos (name STRING,age INT) row format delimited fields terminated by '\t' "); sparkSession.sql("load data local inpath '/root/student_infos' into table student_infos"); //注意:此种方式,程序需要能读取到数据(如/root/student_infos),同时也要能读取到 metastore服务的配置信息。 sparkSession.sql("DROP TABLE IF EXISTS student_scores"); sparkSession.sql("CREATE TABLE IF NOT EXISTS student_scores (name STRING, score INT) row format delimited fields terminated by '\t'"); sparkSession.sql("LOAD DATA " + "LOCAL INPATH '/root/student_scores'" + "INTO TABLE student_scores"); Dataset<Row> sql = sparkSession.sql("SELECT si.name, si.age, ss.score " + "FROM student_infos si " + "JOIN student_scores ss " + "ON si.name=ss.name " + "WHERE ss.score>=80"); sql.registerTempTable("good"); Dataset<Row> sql1 = sparkSession.sql("selelct * from good"); sql1.show(); /** * 将结果保存到hive表 good_student_infos */ sparkSession.sql("DROP TABLE IF EXISTS good_student_infos"); sql1.write().mode(SaveMode.Overwrite).saveAsTable("good_student_infos"); sparkSession.stop();
-
序列化
-
反序列化时 实体类中的serialVersionUID 改变后能否反序列化?(不能)
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时, JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。 当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时, Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。 -
子类实现serializable 父类不实现serializable,当序列化子类时,反序列化得到父类中的某变量的数值时,该变量的数值与序列化时的数值不相同
一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象,然后反序列化后输出父类定义的某变量的数值,
该变量数值与序列化时的数值不同。(需要在父类中是实现默认的构造方法,否则会报异常:no validconstructor)
在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。
- 静态变量能否被序列化?(不能被序列化)
当改变静态变量的值时,序列化后的静态变量也是改变的,如果正常能被序列化的值,是在反序列化中得到的值是不会变的,可见,静态变量不能被序列化。
那为什么反序列化可以读到值呢? 一个静态变量不管是否被transient修饰,均不能被序列化,反序列化后类中static型变量isPerson的值为当前JVM中对应static变量的值,这个值是JVM中的,不是反序列化得出的。 - UDF&UDAF
- UDF:用户自定义函数
- UDAF: 用户自定义聚合函数
initialize:初始化
初始化一个内部自己定义的值,,在Aggregate之前每组数据的初始化结果。
update:更新
一个个的将组内的字段传递进来,实现拼接的逻辑
buffer.getInt(0)获取的是上一次聚合后的值
marge:合并update
将各个节点上分布式拼接好的值,合并起来。
buffer1.getInt(0) : 大聚合的时候 上一次聚合后的值
buffer2.getInt(0) : 这次计算传入进来的update的结果
StructType bufferSchema
在进行聚合操作的时候所要处理的数据的结果的类型
evaluate
最后返回一个和DataType的类型要一致的类型,返回UDAF最后的计算结果
dataType
指定UDAF函数计算后返回的结果类型
inputSchema
指定输入字段的字段及类型
deterministic
确保一致性 一般用true,用以标记针对给定的一组输入,UDAF是否总是生成相同的结果。
- 开窗函数
row_number() 开窗函数是按照某个字段分组,然后取另一字段的前几个的值,相当于 分组取topN。
row_number() over (partitin by XXX order by XXX)