首頁 >Java >java教程 >Parquet Java 中的壓縮演算法

Parquet Java 中的壓縮演算法

Mary-Kate Olsen
Mary-Kate Olsen原創
2025-01-20 18:04:12980瀏覽

Compression algorithms in Parquet Java

Apache Parquet 是一種分析型工作負載的列式儲存格式,但它也可以用於儲存任何類型的結構化數據,從而解決多種用例。

其最顯著的特性之一是能夠在處理過程的兩個階段使用不同的壓縮技術高效地壓縮資料。這降低了儲存成本並提高了讀取效能。

本文解釋了 Java 中 Parquet 的檔案壓縮,提供了使用範例,並分析了其效能。

壓縮技術

與傳統的基於行的儲存格式不同,Parquet 使用列式方法,允許根據相同類型資料的局部性和值冗餘性使用更特定和有效的壓縮技術。

Parquet 以二進位格式寫入訊息,並在兩個不同的層級應用壓縮,每個層級使用不同的技術:

  • 在寫入列的值時,它會根據初始值的特性自適應地選擇編碼類型:字典編碼、遊程編碼、位元打包、增量編碼等。
  • 每當達到一定數量的位元組(預設為 1MB)時,就會形成一個頁面,並且使用程式設計師配置的演算法(無壓縮、GZip、Snappy、LZ4、ZSTD 等)壓縮二進位區塊。

儘管壓縮演算法是在檔案層級配置的,但每列的編碼是使用內部啟發式演算法自動選擇的(至少在 parquet-java 實作中是如此)。

不同壓縮技術的效能在很大程度上取決於您的數據,因此沒有萬能的解決方案可以保證最快的處理時間和最低的儲存空間消耗。 您需要執行自己的測驗

代碼

配置很簡單,只有在寫入時才需要明確設定。讀取檔案時,Parquet 會發現使用了哪種壓縮演算法並應用相應的解壓縮演算法。

設定演算法或編解碼器

在使用 Protocol Buffers 和 Avro 的 Carpet 和 Parquet 中,要設定壓縮演算法,只需呼叫 builder 的 withCompressionCodec 方法:

Carpet

<code class="language-java">CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz)
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

Avro

<code class="language-java">ParquetWriter<Organization> writer = AvroParquetWriter.<Organization>builder(outputFile)
    .withSchema(new Organization().getSchema())
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

Protocol Buffers

<code class="language-java">ParquetWriter<Organization> writer = ProtoParquetWriter.<Organization>builder(outputFile)
    .withMessage(Organization.class)
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

該值必須是 CompressionCodecName 枚舉中可用的值之一:UNCOMPRESSED、SNAPPY、GZIP、LZO、BROTLI、LZ4、ZSTD 和 LZ4_RAW(LZ4 已棄用,應使用 LZ4_RAW)。

壓縮等級

某些壓縮演算法提供了一種微調壓縮等級的方法。此等級通常與它們需要為查找重複模式而付出的努力有關,壓縮等級越高,壓縮過程所需的時間和記憶體就越多。

儘管它們帶有預設值,但可以使用 Parquet 的通用配置機制進行修改,儘管每個編解碼器使用不同的鍵。

此外,要選擇的值不是標準的,並且取決於每個編解碼器,因此您必須參考每個演算法的文件以了解每個等級提供了什麼。

ZSTD

要引用層級的配置,ZSTD 編解碼器宣告一個常數:ZstandardCodec.PARQUET_COMPRESS_ZSTD_LEVEL

可能的值範圍從 1 到 22,預設值為 3。

<code class="language-java">CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz)
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

LZO

要引用層級的配置,LZO 編解碼器宣告一個常數:LzoCodec.LZO_COMPRESSION_LEVEL_KEY

可能的值範圍從 1 到 9、99 和 999,預設值為「999」。

<code class="language-java">ParquetWriter<Organization> writer = AvroParquetWriter.<Organization>builder(outputFile)
    .withSchema(new Organization().getSchema())
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

GZIP

它不會宣告任何常數,您必須直接使用字串“zlib.compress.level”,可能的值範圍從 0 到 9,預設值為“6”。

<code class="language-java">ParquetWriter<Organization> writer = ProtoParquetWriter.<Organization>builder(outputFile)
    .withMessage(Organization.class)
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

效能測試

為了分析不同壓縮演算法的效能,我將使用兩個包含不同類型資料的公共資料集:

  • 紐約市計程車行程:在幾列中包含大量數值和少量字串值。它有 23 列,包含 1960 萬筆記錄。
  • 義大利政府的凝聚力項目:許多欄位包含浮點數值以及大量的各種文字字串。它有 91 列,包含 200 萬行。

我將評估 Parquet Java 中啟用的一些壓縮演算法:UNCOMPRESSED、SNAPPY、GZIP、LZO、ZSTD、LZ4_RAW。

正如預期的那樣,我將使用帶有 parquet-java 提供的預設配置和每種演算法的預設壓縮等級的 Carpet。

您可以在 GitHub 上找到原始程式碼,測試是在配備 AMD Ryzen 7 4800HS CPU 和 JDK 17 的筆記型電腦上完成的。

檔案大小

為了了解每種壓縮的效能,我們將採用等效的 CSV 檔案作為參考。

格式 gov.it 纽约出租车
CSV 1761 MB 2983 MB
未压缩 564 MB 760 MB
SNAPPY 220 MB 542 MB
GZIP **146 MB** 448 MB
ZSTD 148 MB **430 MB**
LZ4_RAW 209 MB 547 MB
LZO 215 MB 518 MB

在這兩個測試中,使用 GZip 和 Zstandard 進行壓縮最為有效率。

僅使用 Parquet 編碼技術,檔案大小可以減少到原始 CSV 大小的 25%-32%。在施加額外壓縮後,它將減少到CSV 大小的 9% 到 15%

寫入

壓縮資訊會帶來多少開銷?

如果我們三次寫入相同的資訊並計算平均秒數,我們會得到:

算法 gov.it 纽约出租车
未压缩 25.0 57.9
SNAPPY 25.2 56.4
GZIP 39.3 91.1
ZSTD 27.3 64.1
LZ4_RAW **24.9** 56.5
LZO 26.0 **56.1**

SNAPPY、LZ4 和 LZO 達到的時間與不壓縮相似,而 ZSTD 會增加一些開銷。 GZIP 效能最差,寫入時間變慢了 50%。

讀取

讀取檔案比寫入更快,因為需要的計算更少。

讀取檔案中的所有列,以秒為單位的時間為:

算法 gov.it 纽约出租车
未压缩 11.4 37.4
SNAPPY **12.5** **39.9**
GZIP 13.6 40.9
ZSTD 13.1 41.5
LZ4_RAW 12.8 41.6
LZO 13.1 41.1

讀取時間接近不壓縮訊息,解壓縮的開銷在 10% 到 20% 之間。

結論

在讀取和寫入時間方面,沒有一種演算法明顯優於其他演算法,所有演算法都在相似的範圍內。 在大多數情況下,壓縮資訊可以彌補空間節省(和傳輸)帶來的時間損失

在這兩個用例中,選擇一種或另一種演算法的決定因素可能是達到的壓縮率,ZSTD 和 Gzip 突出(但寫入時間較差)。

每種演算法都有其優勢,因此最佳選擇是使用您的資料進行測試,考慮哪個因素更重要:

  • 最大限度地減少儲存使用,因為您儲存大量很少使用的資料。
  • 最大限度地減少文件產生時間。
  • 最大限度地減少讀取時間,因為檔案會被多次讀取。

就像生活中的一切一樣,這是一個權衡,您必須看看什麼最能彌補。在 Carpet 中,預設情況下,如果您不配置任何內容,它會使用 Snappy 進行壓縮。

實作細節

該值必須是 CompressionCodecName 枚舉中可用的值之一。與每個枚舉值關聯的是實現演算法的類別的名稱:

<code class="language-java">CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz)
    .withCompressionCodec(CompressionCodecName.ZSTD)
    .build();</code>

Parquet 將使用反射來實例化指定的類,該類必須實作 CompressionCodec 介面。如果您查看其原始程式碼,您會發現它位於 Hadoop 專案中,而不是 Parquet。這顯示 Parquet 在 Java 實作中與 Hadoop 的耦合程度。

要使用其中一種編解碼器,您必須確保已新增包含其實作的 JAR 作為相依性。

並非所有實作都存在於新增 parquet-java 時具有的傳遞依賴項中,或者您可能過於積極地排除了 Hadoop 依賴項。

在 org.apache.parquet:parquet-hadoop 依賴項中,包含 SnappyCodec、ZstandardCodec 和 Lz4RawCodec 的實現,這會傳遞導入 snappy-java、zstd-jni 和 aircompressor 依賴項以及這三種演算法的實際實作。

在 hadoop-common:hadoop-common 依賴項中,包含 GzipCodec 的實作。

BrotliCodec 和 LzoCodec 的實作在哪裡? 它們不在任何 Parquet 或 Hadoop 依賴項中,因此,如果您在不添加其他依賴項的情況下使用它們,則您的應用程式將無法使用那些格式壓縮的檔案。

  • 要支援 LZO,您需要將依賴 org.anarres.lzo:lzo-hadoop 加入到您的 pom 或 gradle 檔案中。
  • Brotli 的情況更為複雜:此依賴項不在 Maven Central 中,您還必須新增 JitPack 儲存庫。

以上是Parquet Java 中的壓縮演算法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn