一.简单介绍
1.RDD
- RDD 是一个懒执行
(只有当遇到action算子时才会真正的开始执行)
的不可变的的 可以支持 lambda 表达式 的并行数据集 - RDD 的最大优势就是简单 , 毕竟容易上手
- RDD 的 劣势在于 , 它是一个存在于 JVM 内存中的对象 , JVM 内存是计算机内存划分的一块空间 , 所以它受到 Gc (
JVM 自己的垃圾处理器 , 用来回收一些不用的对象 , 但是 Spark 框架是没法控制这个 GC 什么时候回收垃圾对象, 很可能这个RDD 已经不使用了 , 但是它仍然存在于 JVM 内存中 , 这样就会占用内存 , 而 JVM 内存又是有大小限制的 , 所以一旦内存过大 , JVM 就会崩溃
) , 而且 当数据增加时 , Java 序列化的成本也相应提高 !
2.DataFrame
- DataFrame 也是懒执行的 , 它的API 更加容易上手 , 也支持 lambda , 并且, DataFrame 没有GC的限制, 因为它是存在于堆外内存的 , 不会受到JVM虚拟机内存的限制, 并且, 垃圾回收也可以由 Spark 框架自己来控制
- DataFrame 相比较于 RDD 增加了数据类型 , 它更像一张表格 如下图
- 对查询进行了一些优化 , 使其性能提高
- DataFrame 的缺点 , 编译期间不会进行类型安全检查 (
就是检查你传入的对象的类型是否和你正确的类型是否一致 , 比如 name 是 String 类型 , 假如你传入一个int 类型的 , 编译期间也不会报错 , 只有运行时才会报错 不知道理解的对不对?
)
3.Dataset
- 拥有 DataFrame 所有优点 , 并且性能比 DataFrame 更强 , API 更人性化 (
暂时没感受到 反正资料是这样写的
) - 拥有类型安全检查
- Dataset支持编解码器,当需要访问非堆上的数据时可以避免反序列化整个对象,提高了效率。(
因为Dataset 的数据时分布在其他不同节点上的 , 在传输过程中需要序列化成 字节流 , 而编码器可以避免反序列化 提高效率
) - JavaBean类被用来在Dataset中定义数据的结构信息,样例类中每个属性的名称直接映射到DataSet中的字段名称。
- (
重点
) Dataframe是Dataset的特列,DataFrame=Dataset[Row] ,所以可以通过as方法将Dataframe转换为Dataset。Row是一个类型,跟Car、Person这些的类型一样,所有的表结构信息我都用Row来表示。 - DataSet是强类型的。比如可以有Dataset[Car],Dataset[Person].
- DataFrame只是知道字段,但是不知道字段的类型,所以在执行这些操作的时候是没办法在编译的时候检查是否类型失败的,比如你可以对一个String进行减法操作,在执行的时候才报错,而DataSet不仅仅知道字段,而且知道字段类型,所以有更严格的错误检查。就跟JSON对象和类对象之间的类比。
二. 创建 RDD DataFrame Dataset
1.所用数据
- person,json
{"name":"Michael", "age":12}
{"name":"Andy", "age":30}
{"name":"Justin", "age":19}
{"name":"kafak", "age":19}
- person.txt
Michael, 29
Andy, 30
Justin, 19
2. 创建 RDD DataFrame Dataset
- JavaBean 用来封装数据
// 切记 需要实现 Serializable !!! Person 的类修饰符是public
public static class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 创建RDD
这个比较简单 , 直接使用 对应方法读取数据即可返回一个RDD
这里注意 直接 spark.read().textFile() 读取返回的是一个 DataFrame 对象
// 1.从一个已经存在的文件读取数据 并创建一个存储 Person 对象的RDD
JavaRDD<String> dataRDD = spark
.read()
.textFile("SparkSql/src/main/resources/people.txt")
.javaRDD();
- 创建一个 DataFrame
JavaRDD<String> dataRDD = spark
.read()
.textFile("SparkSql/src/main/resources/people.txt");
- RDD 转化为 DataFrame
// 2.将 people.txt 进行切分 , 并将对应的 数据封装到 Person
/**
* 将读取到的数据封装到 Person对象中 , 然后再将 Person 存储到新的RDD 中
* dataRDD -> Michael, 29 -> Person(Michael, 29) -> peopleRDD<Person>
*/
/*JavaRDD<Person> peopleRDD = dataRDD.map(new Function<String, Person>() {
@Override
public Person call(String v1) throws Exception {
// 对数据进行切分
String[] parts = v1.split(",");
Person person = new Person();
person.setName(parts[0]);
person.setAge(Integer.parseInt(parts[1].trim())); // 注意去除空格
return person;
}
});*/
// 注意 上面注释的是以前的写法 , 这里我们可以使用 lamda 表达式 如下 : 用法和 scala 相似
JavaRDD<Person> peopleRDD = dataRDD
.map(line ->{
String[] parts = line.split(",");
Person person = new Person();
person.setName(parts[0]);
person.setAge(Integer.parseInt(parts[1].trim())); // 注意去除空格
return person;
});
// 将 RDD 转化为一个DataFrame
// 为了容易区分 命名时 : peopleDF 代表 DataFrame peopleDS 代表Dataset
Dataset<Row> peopleDF = spark
// 这里 CreateDataFrame(RDD , JavaBean对象.class) 如下
// JavaBean 对象就是你把数据封装到的那个对象
.createDataFrame(peopleRDD, Person.class);
如上 : 这里需要注意的是 , 因为我把读取的数据封装到 Person 对象中 所以代码比较多
实际上转化很简单 完整代码如下
JavaRDD<String> dataRDD = spark
.read()
.textFile("SparkSql/src/main/resources/people.txt")
.javaRDD();
// 注意 RDD 内存储是xxx类型的 , 后面就写 xxx.class
Dataset<Row> peopleDF = spark.createDataFrame(dataRDD, String.class);
// 创建一个临时表
peopleDF.createOrReplaceTempView("people");
// 进行查询
// 注意 : 必须先创建表才能查询 , 查询语句的方法在 SparkSession
// SparkSession的理解 : 可以认为是你在 windows 用 连接数据库工具的一次连接
// 然后SparkSession的一些方法可以理解成对应的 一些功能 , 比如 sql()
// 可以理解成开启了一个查询窗口 ..
// 使用Sql 语句进行查询
// 注意 这个结果是包含所有 年龄在 13 到 19 的人的名字
Dataset<Row> teenagersDF = spark
.sql("SELECT name FROM people WERE age BETWEEN 13 AND 19");
- DataFrame 转化成 Dataset
// 重点 !!!!! 如何将 DataFrame 转化为 Dataset
// 1) 第一步 需要有个对应类型的编码器
// 这里,由于name都是String 类型的 我们可以使用一个 String 类型的编码器
// 1.创建一个String类型的编码器
// 创建方式都是一样的 这里我多举几个例子 , 便于理解
Encoder<String> stringEncoder = Encoders.STRING();
// 2.创建一个 Integer 类型的编码器
Encoder<Integer> integerEncoder = Encoders.INT();
// 3.创建一个 自定义类型的编码器 , 比如 Person 这里需要特别注意 参数填的是 类名.class
Encoder<Person> personEncoder = Encoders.bean(Person.class);
// 2) 第二步 , 将对应的DataFrame 转化为 Dataset
Dataset<String> teenagersDS = teenagersDF.as(stringEncoder);
这里特别注意 : DataFrame 是特殊的Dataset 因为所有的数据都只有一个类型就是
Dataset<Row>
这一个类型 , 而 Dataset有很多类型 比如 Dataset<String> Dataset<Person>