引言
HDFS上分布式文件存储,成为大数据平台首选存储平台。而Spark往往以HDFS文件为输入,为保持兼容性,Spark支持多种格式文件读取,大数据场景下,性能瓶颈往往是IO,而不是CPU算力,所以对文件的压缩处理成为了很必要的手段。Spark为提供兼容性,同时支持多种压缩包直接读取,方便于用户使用,不用提前对压缩格式处理,但各种压缩格式各有优缺点,若不注意将导致Spark的能力无法发挥出来。故,对Spark计算压缩文件做一个分析。
支持的压缩格式
首先来看一下Spark读取HDFS文件常用的压缩格式:
存储格式 | 优点 | 缺点 | 是否可切分 | 建议用途 | 备注 |
---|---|---|---|---|---|
GZIP | 压缩率高 | CPU使用率高,压缩慢 | 否 | 冷数据 | |
BZIP2 | 压缩率高,部分文件格式甚至比GZIP高 | CPU使用率高,压缩慢,HBase不支持 | 是 | 冷数据 | |
LZO | 压缩快 | 压缩率低,原生不支持,需要额外安装 | 是 | 热数据 | 因为使用GPL协议,所以一般不自带,有条件可提前分割文件,适合于MR任务 |
LZ4 | 压缩快,解压速度比LZO更快 | 压缩率比LZO略低 | 否 | 热数据 | |
SNAPPY | 压缩快,普遍比LZO更快,原生支持 | 压缩率低 | 否 | 热数据 | HDFS使用最广泛的压缩格式,但不可拆分。但是在Container file format里面的Snappy块是可以拆分的,例如Avro和SequenceFile。Snappy一般也需要和一个Container file format一起使用[1] |
[1] :Avro和SequenceFile为另外两种压缩格式,一般结合Snappy做分块压缩,但目前没有找到相关资料。
执行对比分析
- 实验数据:同一个文件包,json格式文件数据
- 处理逻辑:增加列,然后发送到kafka中。
- DAG逻辑划分:两个job(read动作一个job,foreach动作一个job),每个job下面各一个stage,每个stage下面task若干
- 程序执行参数:–master yarn --deploy-mode client --executor-cores 4 --executor-memory 4G --num-executors 4
- Spark逻辑代码:
df = spark.read.json("\data\2019\08\23\*****.json")
udf_parse_os = udf(parse_os, StringType())
df = df.select(df.ip, df.port, df.data, df.host, udf_parse_os(df.data).alias("os"))
df.foreachPartition(send_message_helper)
非压缩文件
文件大小:33.7GB
运行时间:9min
read阶段:
可以看到所有节点都在读取,分布式读取,速度很快。
Stage里面共计分成了252个task,每个读取128MB数据。
foreach阶段
依然并行全力计算
每个执行节点上4个core都在并发运行。
GZIP
文件大小:10.6GB
运行时间:2.2h
read阶段
只有单节点读取
同时该节点上也只有一个核心在运行
foreach阶段
也是只有单节点、单core运行
BZIP2
文件大小:7.7GB
运行时间:12min
read阶段
与非压缩一致,并行进行
foreach阶段
同样并发执行
SNAPPY
文件大小:16.5GB
运行时间:2.1h
这里直接采用的整文件压缩,所以文件不可分割。
read阶段
单节点单核心读取,非并行。
foreach阶段
同上类似,单节点单核心运行
结果
压缩格式 | 文件体积 | 运行时间 | 并发数 |
---|---|---|---|
非压缩 | 33.7GB | 9min | 16 |
GZIP | 10.6GB | 2.2h | 1 |
BZ2 | 7.7GB | 12min | 16 |
SNAPPY | 16.5GB | 2.1h | 1 |
gzip和snappy无法采用并行计算,也就是说在spark平台上,这两种格式只能采用串行单进程执行,于本文开头表格对应,无法分割(splittable)的压缩格式只能顺序一个进程读取,而读取后多文件又在一个executor上,其他executor无文件导致无法并行的foreach。
bz2和非压缩格式支持分割,也就是说可以并行读取以及计算。
不可分割的压缩格式文件不可并行读取,完全无法发挥spark的并行计算优势,并且若压缩包过大,对单节点的物理性能要求较高。
建议
snappy采用分块压缩方式使其可以并行读取计算。
gzip格式最好提前进行分割成小文件或者换格式,因多个文件可以并行读取。另一个办法是read文件后调用repartition操作强制将读取多数据重新均匀分配到不同的executor上,但这个操作会导致大量单节点性能占用,因此该格式建议不在spark上使用。
bz2表现相同于非压缩,但解压操作需要耗费时间。
非压缩性能表现最佳,但会占用过大HDFS存储。