目标
理解
Spark
读写Parquet
文件的语法理解
Spark
读写Parquet
文件的时候对于分区的处理
什么时候会用到 Parquet
?
在 ETL
中, Spark
经常扮演 T
的职务, 也就是进行数据清洗和数据转换.
为了能够保存比较复杂的数据, 并且保证性能和压缩率, 通常使用 Parquet
是一个比较不错的选择.
所以外部系统收集过来的数据, 有可能会使用 Parquet
, 而 Spark
进行读取和转换的时候, 就需要支持对 Parquet
格式的文件的支持.
使用代码读写 Parquet
文件
默认不指定 format
的时候, 默认就是读写 Parquet
格式的文件
@Test
def parquet(): Unit={
//1.读取csv文件的数据
val df = spark.read.option("header",value = true).csv("dataset/BeijingPM20100101_20151231.csv")
//2.把数据写为Parquet格式
// 写入时:spark默认的文件格式parquet
// 写入模式:报错,覆盖,追加,忽略
df.write
.format("parquet")
.mode(SaveMode.Overwrite)
.save("dataset/beijing_pm3")
//3.读取Parquet格式文件
// 默认格式是parquet
// 可以读取文件夹
spark.read
.load("dataset/beijing_pm3")
.show()
}
写入 Parquet
的时候可以指定分区
Spark
在写入文件的时候是支持分区的, 可以像 Hive
一样设置某个列为分区列
/**
* 表分区的概念不仅在parquet上有,其它格式的文件也可以指定表分区
*/
@Test
def parquetPartitions():Unit = {
//1.读取数据
val df = spark.read
.option("header",value = true)
.csv("dataset/BeijingPM20100101_20151231.csv")
//2.写文件,表分区
df.write
.partitionBy("year","month")
.save("dataset/beijing_pm4")
//3.读文件,自动发现分区
//写分区表的时候,分区列不会包含在生成的文件中
//直接通过文件来进行读取的话,分区信息会丢失
//spark sql会进行自动的分区发现
spark.read
.parquet("dataset/beijing_pm4")
.printSchema()
}
这个地方指的分区是类似 Hive 中表分区的概念, 而不是 RDD 分布式分区的含义
分区发现
在读取常见文件格式的时候, Spark
会自动的进行分区发现, 分区自动发现的时候, 会将文件名中的分区信息当作一列. 例如 如果按照性别分区, 那么一般会生成两个文件夹 gender=male
和 gender=female
, 那么在使用 Spark
读取的时候, 会自动发现这个分区信息, 并且当作列放入创建的 DataFrame
中
使用代码证明这件事可以有两个步骤, 第一步先读取某个分区的单独一个文件并打印其 Schema
信息, 第二步读取整个数据集所有分区并打印 Schema
信息, 和第一步做比较就可以确定
val spark = ...
val partDF = spark.read.load("dataset/beijing_pm/year=2010/month=1")
partDF.printSchema()
把分区的数据集中的某一个区单做一整个数据集读取, 没有分区信息, 自然也不会进行分区发现
val df = spark.read.load("dataset/beijing_pm")
df.printSchema()
此处读取的是整个数据集, 会进行分区发现, DataFrame 中会包含分去列
配置 | 默认值 | 含义 |
---|---|---|
|
|
一些其他 |
|
|
一些其他 |
|
|
打开 Parquet 元数据的缓存, 可以加快查询静态数据 |
|
|
压缩方式, 可选 |
|
|
当为 true 时, Parquet 数据源会合并从所有数据文件收集的 Schemas 和数据, 因为这个操作开销比较大, 所以默认关闭 |
|
|
如果为 |
总结
Spark
不指定format
的时候默认就是按照Parquet
的格式解析文件
Spark
在读取Parquet
文件的时候会自动的发现Parquet
的分区和分区字段
Spark
在写入Parquet
文件的时候如果设置了分区字段, 会自动的按照分区存储
目标
理解
JSON
的使用场景能够使用
Spark
读取处理JSON
格式文件
什么时候会用到 JSON
?
在 ETL
中, Spark
经常扮演 T
的职务, 也就是进行数据清洗和数据转换.
在业务系统中, JSON
是一个非常常见的数据格式, 在前后端交互的时候也往往会使用 JSON
, 所以从业务系统获取的数据很大可能性是使用 JSON
格式, 所以就需要 Spark
能够支持 JSON 格式文件的读取
读写 JSON
文件
将要 Dataset
保存为 JSON
格式的文件比较简单, 是 DataFrameWriter
的一个常规使用
@Test
def json():Unit={
val df = spark.read
.option("header",value = true)
.csv("dataset/BeijingPM20100101_20151231.csv")
df.write.json("dataset/beijing_pm5.json")
spark.read.json("dataset/beijing_pm5.json").show()
}
如果不重新分区, 则会为 DataFrame 底层的 RDD 的每个分区生成一个文件, 为了保持只有一个输出文件, 所以重新分区
保存为 JSON 格式的文件有一个细节需要注意, 这个 JSON 格式的文件中, 每一行是一个独立的 JSON, 但是整个文件并不只是一个 JSON 字符串, 所以这种文件格式很多时候被成为 JSON Line 文件, 有时候后缀名也会变为 jsonl
beijing_pm.jsonl 如下:
{"day":"1","hour":"0","season":"1","year":2013,"month":3}
{"day":"1","hour":"1","season":"1","year":2013,"month":3}
{"day":"1","hour":"2","season":"1","year":2013,"month":3}
也可以通过 DataFrameReader
读取一个 JSON Line
文件
val spark: SparkSession = ...
val dfFromJSON = spark.read.json("dataset/beijing_pm_json")
dfFromJSON.show()
JSON
格式的文件是有结构信息的, 也就是 JSON
中的字段是有类型的, 例如 "name": "zhangsan"
这样由双引号包裹的 Value
, 就是字符串类型, 而 "age": 10
这种没有双引号包裹的就是数字类型, 当然, 也可以是布尔型 "has_wife": true
Spark
读取 JSON Line
文件的时候, 会自动的推断类型信息
val spark: SparkSession = ...
val dfFromJSON = spark.read.json("dataset/beijing_pm_json")
dfFromJSON.printSchema()
Spark
可以从一个保存了 JSON
格式字符串的 Dataset[String]
中读取 JSON
信息, 转为 DataFrame
这种情况其实还是比较常见的, 例如如下的流程
假设业务系统通过 Kafka
将数据流转进入大数据平台, 这个时候可能需要使用 RDD
或者 Dataset
来读取其中的内容, 这个时候一条数据就是一个 JSON
格式的字符串, 如何将其转为 DataFrame
或者 Dataset[Object]
这样具有 Schema
的数据集呢? 使用如下代码就可以
/**
* toJSON的场景:
* 处理完成后,DataFrame中如果是一个对象,如果其他的系统只支持JSON格式的数据
* SparkSQL 如果和这种系统进行整合的时候,就需要进行转换
*/
@Test
def json1():Unit={
val df = spark.read
.option("header",value = true)
.csv("dataset/BeijingPM20100101_20151231.csv")
df.toJSON.show()
}
/**
* 从消息队列中取出JSON格式的数据,需要在SparkSQL中处理
*/
@Test
def json2():Unit={
val df = spark.read
.option("header",value = true)
.csv("dataset/BeijingPM20100101_20151231.csv")
val jsonRdd: RDD[String] = df.toJSON.rdd
spark.read.json(jsonRdd).show()
}
总结
JSON
通常用于系统间的交互,Spark
经常要读取JSON
格式文件, 处理, 放在另外一处使用
DataFrameReader
和DataFrameWriter
可以轻易的读取和写入JSON
, 并且会自动处理数据类型信息