Apache Parquet ist ein spaltenorientiertes Speicherformat, das auf analytische Arbeitslasten abzielt, aber es kann zum Speichern jeder Art von strukturierten Daten verwendet werden und eine Vielzahl von Anwendungsfällen abdecken.
Eines der bemerkenswertesten Merkmale ist die Fähigkeit, Daten mithilfe verschiedener Komprimierungstechniken in beiden Phasen des Verarbeitungsprozesses effizient zu komprimieren. Dies reduziert die Speicherkosten und verbessert die Leseleistung.
In diesem Artikel wird die Dateikomprimierung von Parquet in Java erläutert, Anwendungsbeispiele bereitgestellt und die Leistung analysiert.
Im Gegensatz zu herkömmlichen zeilenbasierten Speicherformaten verwendet Parquet einen spaltenbasierten Ansatz, der die Verwendung spezifischerer und effizienterer Komprimierungstechniken ermöglicht, die auf Lokalität und Wertredundanz desselben Datentyps basieren.
Parquet schreibt Informationen im Binärformat und wendet die Komprimierung auf zwei verschiedenen Ebenen an, wobei jede eine andere Technik verwendet:
Obwohl der Komprimierungsalgorithmus auf Dateiebene konfiguriert wird, wird die Kodierung jeder Spalte automatisch mithilfe einer internen Heuristik ausgewählt (zumindest in der Parquet-Java-Implementierung).
Die Leistung verschiedener Komprimierungstechnologien hängt stark von Ihren Daten ab, daher gibt es keine einheitliche Lösung, die die schnellste Verarbeitungszeit und den niedrigsten Speicherverbrauch garantiert. Sie müssen Ihre eigenen Tests durchführen.
Die Konfiguration ist einfach und erfordert nur eine explizite Einstellung beim Schreiben. Beim Lesen einer Datei erkennt Parquet, welcher Komprimierungsalgorithmus verwendet wird, und wendet den entsprechenden Dekomprimierungsalgorithmus an.
In Teppich und Parkett mit Protokollpuffern und Avro rufen Sie zum Konfigurieren des Komprimierungsalgorithmus einfach die withCompressionCodec-Methode des Builders auf:
Teppich
CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
Avro
ParquetWriter<Organization> writer = AvroParquetWriter.<Organization>builder(outputFile) .withSchema(new Organization().getSchema()) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
Protokollpuffer
ParquetWriter<Organization> writer = ProtoParquetWriter.<Organization>builder(outputFile) .withMessage(Organization.class) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
Der Wert muss einer der in der Aufzählung „CompressionCodecName“ verfügbaren Werte sein: UNCOMPRESSED, SNAPPY, GZIP, LZO, BROTLI, LZ4, ZSTD und LZ4_RAW (LZ4 ist veraltet, LZ4_RAW sollte verwendet werden).
Einige Komprimierungsalgorithmen bieten eine Möglichkeit zur Feinabstimmung der Komprimierungsstufe. Diese Stufe hängt in der Regel davon ab, wie viel Aufwand sie in die Suche nach sich wiederholenden Mustern investieren müssen. Je höher die Komprimierungsstufe, desto mehr Zeit und Speicher erfordert der Komprimierungsprozess.
Obwohl sie mit Standardwerten geliefert werden, können sie mit dem generischen Konfigurationsmechanismus von Parquet geändert werden, allerdings unter Verwendung unterschiedlicher Schlüssel für jeden Codec.
Darüber hinaus sind die auszuwählenden Werte nicht standardisiert und hängen von jedem Codec ab. Daher müssen Sie die Dokumentation für jeden Algorithmus konsultieren, um zu verstehen, was jede Ebene bietet.
ZSTD
Zur Referenzebenenkonfiguration deklariert der ZSTD-Codec eine Konstante: ZstandardCodec.PARQUET_COMPRESS_ZSTD_LEVEL
.
Mögliche Werte liegen zwischen 1 und 22, der Standardwert ist 3.
CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
LZO
Zur Referenzebenenkonfiguration deklariert der LZO-Codec eine Konstante: LzoCodec.LZO_COMPRESSION_LEVEL_KEY
.
Mögliche Werte reichen von 1 bis 9, 99 und 999, wobei der Standardwert „999“ ist.
ParquetWriter<Organization> writer = AvroParquetWriter.<Organization>builder(outputFile) .withSchema(new Organization().getSchema()) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
GZIP
Es werden keine Konstanten deklariert, Sie müssen die Zeichenfolge „zlib.compress.level“ direkt verwenden, mögliche Werte reichen von 0 bis 9, der Standardwert ist „6“.
ParquetWriter<Organization> writer = ProtoParquetWriter.<Organization>builder(outputFile) .withMessage(Organization.class) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
Um die Leistung verschiedener Komprimierungsalgorithmen zu analysieren, verwende ich zwei öffentliche Datensätze, die unterschiedliche Datentypen enthalten:
Ich werde einige der in Parquet Java aktivierten Komprimierungsalgorithmen bewerten: UNCOMPRESSED, SNAPPY, GZIP, LZO, ZSTD, LZ4_RAW.
Wie erwartet verwende ich Carpet mit der von parquet-java bereitgestellten Standardkonfiguration und der Standardkomprimierungsstufe für jeden Algorithmus.
Den Quellcode finden Sie auf GitHub, der Test wurde auf einem Laptop mit einer AMD Ryzen 7 4800HS CPU und JDK 17 durchgeführt.
Um die Leistung jeder Komprimierung zu verstehen, verwenden wir die entsprechende CSV-Datei als Referenz.
格式 | 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 |
Von den beiden Tests war die Komprimierung mit GZip und Zstandard am effizientesten.
Wenn nur die Parquet-Kodierungstechnologie verwendet wird, kann die Dateigröße auf 25–32 % der ursprünglichen CSV-Größe reduziert werden. Bei zusätzlicher Komprimierung wird sie auf 9 % bis 15 % der CSV-Größe reduziert.
Wie viel Overhead bringt die Komprimierung von Informationen mit sich?
Wenn wir die gleichen Informationen dreimal schreiben und die durchschnittlichen Sekunden berechnen, erhalten wir:
算法 | 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 und LZO erreichen ähnliche Zeiten wie bei keiner Komprimierung, während ZSTD etwas Overhead verursacht. GZIP hatte die schlechteste Leistung, die Schreibzeiten verlangsamten sich um 50 %.
Das Lesen einer Datei ist schneller als das Schreiben, da weniger Rechenaufwand erforderlich ist.
Die Zeit in Sekunden zum Lesen aller Spalten in der Datei beträgt:
算法 | 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 |
Die Lesezeit liegt nahe an der von unkomprimierten Informationen und der Dekomprimierungsaufwand liegt zwischen 10 % und 20 %.
Kein Algorithmus ist hinsichtlich der Lese- und Schreibzeiten deutlich besser als die anderen, alle liegen in einem ähnlichen Bereich. In den meisten Fällen kann die Komprimierung von Informationen die Platzersparnis (und die verlorene Übertragungszeit) ausgleichen.
In diesen beiden Anwendungsfällen ist wahrscheinlich das erreichte Komprimierungsverhältnis der entscheidende Faktor für die Wahl des einen oder anderen Algorithmus, wobei ZSTD und Gzip im Vordergrund stehen (aber die Schreibzeiten sind schlechter).
Jeder Algorithmus hat seine Vorteile, daher ist es am besten, ihn mit Ihren Daten zu testen und zu überlegen, welcher Faktor wichtiger ist:
Wie alles im Leben ist es ein Kompromiss und man muss sehen, was ihn am besten ausgleicht. In Carpet wird standardmäßig Snappy zur Komprimierung verwendet, wenn Sie nichts konfigurieren.
Der Wert muss einer der in der Aufzählung „CompressionCodecName“ verfügbaren Werte sein. Jedem Aufzählungswert ist der Name der Klasse zugeordnet, die den Algorithmus implementiert:
CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz) .withCompressionCodec(CompressionCodecName.ZSTD) .build();
Parquet verwendet Reflektion, um die angegebene Klasse zu instanziieren, die die CompressionCodec-Schnittstelle implementieren muss. Wenn Sie sich den Quellcode ansehen, werden Sie feststellen, dass er sich im Hadoop-Projekt und nicht in Parquet befindet. Dies zeigt, wie gut Parquet in seiner Java-Implementierung an Hadoop gekoppelt ist.
Um einen dieser Codecs zu verwenden, müssen Sie sicherstellen, dass Sie das JAR, das seine Implementierung enthält, als Abhängigkeit hinzugefügt haben.
In den transitiven Abhängigkeiten, die Sie beim Hinzufügen von Parquet-Java haben, sind nicht alle Implementierungen vorhanden, oder Sie schließen Hadoop-Abhängigkeiten möglicherweise zu aggressiv aus.
In der Abhängigkeit org.apache.parquet:parquet-hadoop sind Implementierungen von SnappyCodec, ZstandardCodec und Lz4RawCodec enthalten, die die Abhängigkeiten snappy-java, zstd-jni und aircompressor zusammen mit den tatsächlichen Implementierungen dieser drei Algorithmen transitiv importieren .
Enthält in der Abhängigkeit hadoop-common:hadoop-common die Implementierung von GzipCodec.
Wo sind die Implementierungen von BrotliCodec und LzoCodec? Sie befinden sich in keinen Parquet- oder Hadoop-Abhängigkeiten. Wenn Sie sie also verwenden, ohne zusätzliche Abhängigkeiten hinzuzufügen, kann Ihre Anwendung keine in diesen Formaten komprimierten Dateien verwenden.
Das obige ist der detaillierte Inhalt vonKomprimierungsalgorithmen in Parquet Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!