Spark SQL - gzip、snappy和lzo压缩格式之间的区别

40

我正在尝试使用Spark SQL编写parquet文件。

默认情况下,Spark SQL支持gzip,但它也支持其他压缩格式,如snappylzo

这些压缩格式有什么区别?


2
默认情况下,Spark 使用“snappy”而不是“gzip”。至少在 s3 上是这样的:文件名中包含字符串“snappy”。 - bashan
@bashan:最近的Spark版本将默认格式更改为Snappy,但在1.6.1之前,我可以看到默认的Parquet压缩格式是gzip。 - Shankar
5个回答

51

压缩比: GZIP 压缩比较 Snappy 或 LZO 更高,但使用的 CPU 资源也更多。

一般用途: 对于不经常访问的数据,GZip 是一个不错的选择。 而对于经常访问的数据,Snappy 或 LZO 更加合适。

Snappy 的性能通常优于 LZO。值得运行测试以查看是否存在显著差异。

可分割性: 如果需要将压缩数据分割,BZip2、LZO 和 Snappy 格式都是可分割的,但 GZip 不支持。

GZIP 相对于 Snappy 可以将数据压缩 30%,但读取 GZIP 数据时消耗的 CPU 是 Snappy 的两倍。

LZO 专注于在低 CPU 使用率下提供快速解压缩和更高的压缩比,但代价是更多的 CPU 消耗。

对于长期/静态存储,GZip 压缩仍然更好。

本文详细介绍了多种通用压缩算法的性能测试和基准代码及结果(其中一些算法速度之快令人难以置信!)。

enter image description here


我无法对您的好文章提出修改建议,但我想指出bz2确实可以本地分割,但Snappy和LZO需要hadoop进行分割。如果不是使用hadoop库中提供的工具生成,则它们很可能无法被分割。 - Kiwy
很不幸,文章的链接已经失效了,你知道它被转移到哪里了吗? - Daniel Habenicht

37

如果你可以承受更高的磁盘使用量以获得性能提升(更低的CPU使用率+可分割性),则应使用Snappy。

当Spark将默认压缩方式从GZIP切换为Snappy时,其原因是:

根据我们的测试,gzip解压速度非常慢(<100MB/s), 导致查询过程处于解压绑定状态。 Snappy可以在单核心上以~500MB/s的速度解压缩。

Snappy:

  • 存储空间:高
  • CPU使用率:低
  • 可分割性:(1)

GZIP:

  • 存储空间:中等
  • CPU使用率:中等
  • 可分割性:

1)http://boristyukin.com/is-snappy-compressed-parquet-file-splittable/


5
这段话的意思是,“Snappy和GZip块不可分割,但包含Snappy块的容器文件格式(如SequenceFile或Avro)可以进行分割。” 这里讨论的是Parquet格式。在Parquet中使用Snappy时,它是可分割的,正如答案所述。 - Garren S
1
没错,抱歉我错过了问题的最后一部分。谢谢伙计。 - Maziyar
根据此 https://dev59.com/KlgQ5IYBdhLWcg3wGgBY#43390870,parquet文件是可分割的,与使用的压缩方式无关,因此我不确定比较中的第三点是否成立。 - RiskyMaor

27

只需在您的数据上尝试它们。

lzo和snappy是快速压缩器和非常快的解压器,但与gzip相比具有较少的压缩率,而gzip则具有更好的压缩率,但稍微慢一些。

多年后的更新:

还应尝试lz4zstd


9

根据以下数据,我认为在流媒体等需要重视写入时间延迟的场景之外,gzip 是胜出的。

需要记住的是,速度本质上就是计算成本。然而,云计算只需一次付费,而云存储则需要循环性的支付成本。权衡取舍取决于数据保留期。


我们使用大型和小型parquet文件在Python中测试速度和大小。

结果(大文件,117 MB):

        +----------+----------+--------------------------+
        | snappy   | gzip     | snappy/gzip              |
+-------+----------+----------+--------------------------+
| write | 1.62 ms  | 7.65 ms  | 4.7x faster              |
+-------+----------+----------+--------------------------+
| size  | 35484122 | 17269656 |    2x larger             |
+-------+----------+----------+--------------------------+
| read  | 973 ms   | 1140 ms  |  1.2x faster             |
+-------+----------+----------+--------------------------+

结果(小文件,4 KB,鸢尾花数据集):

        +---------+---------+--------------------------+
        | snappy  | gzip    | snappy/gzip               |
+-------+---------+---------+--------------------------+
| write | 1.56 ms | 2.09 ms |  1.3x faster             |
+-------+---------+---------+--------------------------+
| size  | 6990    | 6647    |  5.2% smaller            |
+-------+---------+---------+--------------------------+
| read  | 3.22 ms | 3.44 ms |  6.8% slower             |
+-------+---------+---------+--------------------------+

small_file.ipynb

import os, sys
import pyarrow
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris

iris = load_iris()

df = pd.DataFrame(
    data= np.c_[iris['data'], iris['target']],
    columns= iris['feature_names'] + ['target']
)

# ========= WRITE =========
%timeit df.to_parquet(path='iris.parquet.snappy', compression='snappy', engine='pyarrow', index=True)
# 1.56 ms

%timeit df.to_parquet(path='iris.parquet.gzip', compression='snappy', engine='pyarrow', index=True)
# 2.09 ms

# ========= SIZE =========
os.stat('iris.parquet.snappy').st_size
# 6990

os.stat('iris.parquet.gzip').st_size
# 6647

# ========= READ =========
%timeit pd.read_parquet(path='iris.parquet.snappy', engine='pyarrow')
# 3.22 ms

%timeit pd.read_parquet(path='iris.parquet.gzip', engine='pyarrow')
# 3.44 ms

large_file.ipynb

import os, sys
import pyarrow
import pandas as pd

df = pd.read_csv('file.csv')

# ========= WRITE =========
%timeit df.to_parquet(path='file.parquet.snappy', compression='snappy', engine='pyarrow', index=True)
# 1.62 s

%timeit df.to_parquet(path='file.parquet.gzip', compression='gzip', engine='pyarrow', index=True)
# 7.65 s

# ========= SIZE =========
os.stat('file.parquet.snappy').st_size
# 35484122

os.stat('file.parquet.gzip').st_size
# 17269656

# ========= READ =========
%timeit pd.read_parquet(path='file.parquet.snappy', engine='pyarrow')
# 973 ms

%timeit pd.read_parquet(path='file.parquet.gzip', engine='pyarrow')
# 1.14 s

1

我同意第一个答案(@Mark Adler)并提供一些研究信息[1],但我不同意第二个答案(@Garren S)[2]。也许Garren误解了问题,因为: [2] Parquet支持所有支持的编解码器分割:在HDFS中使用Spark时,是否可以将gzip压缩的Parquet文件分割? ,Tom White的《Hadoop权威指南》第四版,第5章:Hadoop I/O,第106页。 [1] 我的研究: 源数据-205 GB。文本(分隔字段),未压缩。 输出数据:

<!DOCTYPE html>
<html>

<head>
  <style>
    table,
    th,
    td {
      border: 1px solid black;
      border-collapse: collapse;
    }
  </style>
</head>

<body>

  <table style="width:100%">
    <tr>
      <th></th>
      <th>time of computing, hours</th>
      <th>volume, GB</th>
    </tr>
    <tr>
      <td>ORC with default codec</td>
      <td>3-3,5</td>
      <td>12.3</td>
    </tr>
    <tr>
      <td>Parquet with GZIP</td>
      <td>3,5-3,7</td>
      <td>12.9</td>
    </tr>
    <tr>
      <td>Parquet with SNAPPY</td>
      <td>2,5-3,0</td>
      <td>60.4</td>
    </tr>
  </table>

</body>

</html>

使用Hive在由2个m4.16xlarge组成的EMR上执行了转换。 转换-选择所有字段并按多个字段排序。 当然,这项研究并不标准,但至少在一定程度上展示了真实的比较。使用其他数据集和计算结果可能会有所不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接