Apache Parquet 是一種分析型工作負載的列式儲存格式,但它也可以用於儲存任何類型的結構化數據,從而解決多種用例。
其最顯著的特性之一是能夠在處理過程的兩個階段使用不同的壓縮技術高效地壓縮資料。這降低了儲存成本並提高了讀取效能。
本文解釋了 Java 中 Parquet 的檔案壓縮,提供了使用範例,並分析了其效能。
與傳統的基於行的儲存格式不同,Parquet 使用列式方法,允許根據相同類型資料的局部性和值冗餘性使用更特定和有效的壓縮技術。
Parquet 以二進位格式寫入訊息,並在兩個不同的層級應用壓縮,每個層級使用不同的技術:
儘管壓縮演算法是在檔案層級配置的,但每列的編碼是使用內部啟發式演算法自動選擇的(至少在 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>
為了分析不同壓縮演算法的效能,我將使用兩個包含不同類型資料的公共資料集:
我將評估 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 依賴項中,因此,如果您在不添加其他依賴項的情況下使用它們,則您的應用程式將無法使用那些格式壓縮的檔案。
以上是Parquet Java 中的壓縮演算法的詳細內容。更多資訊請關注PHP中文網其他相關文章!