Apache Parquet est un format de stockage en colonnes destiné aux charges de travail analytiques, mais il peut être utilisé pour stocker tout type de données structurées, répondant à une variété de cas d'utilisation.
L'une de ses fonctionnalités les plus remarquables est la capacité de compresser efficacement les données en utilisant différentes techniques de compression aux deux étapes du processus de traitement. Cela réduit les coûts de stockage et améliore les performances de lecture.
Cet article explique la compression de fichiers de Parquet en Java, fournit des exemples d'utilisation et analyse ses performances.
Contrairement aux formats de stockage traditionnels basés sur les lignes, Parquet utilise une approche en colonnes, permettant l'utilisation de techniques de compression plus spécifiques et efficaces basées sur la localité et la redondance des valeurs du même type de données.
Parquet écrit les informations au format binaire et applique une compression à deux niveaux différents, chacun utilisant une technique différente :
Bien que l'algorithme de compression soit configuré au niveau du fichier, l'encodage de chaque colonne est automatiquement sélectionné à l'aide d'une heuristique interne (au moins dans l'implémentation parquet-java).
Les performances des différentes technologies de compression dépendent fortement de vos données. Il n'existe donc pas de solution universelle garantissant le temps de traitement le plus rapide et la consommation de stockage la plus faible. Vous devez effectuer vos propres tests.
La configuration est simple et ne nécessite qu'un paramétrage explicite lors de l'écriture. Lors de la lecture d'un fichier, Parquet découvre quel algorithme de compression est utilisé et applique l'algorithme de décompression correspondant.
Dans Carpet and Parquet utilisant Protocol Buffers et Avro, pour configurer l'algorithme de compression, il suffit d'appeler la méthode withCompressionCodec du constructeur :
Tapis
<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>
Tampons de protocole
<code class="language-java">ParquetWriter<Organization> writer = ProtoParquetWriter.<Organization>builder(outputFile) .withMessage(Organization.class) .withCompressionCodec(CompressionCodecName.ZSTD) .build();</code>
La valeur doit être l'une des valeurs disponibles dans l'énumération CompressionCodecName : UNCOMPRESSED, SNAPPY, GZIP, LZO, BROTLI, LZ4, ZSTD et LZ4_RAW (LZ4 est obsolète, LZ4_RAW doit être utilisé).
Certains algorithmes de compression permettent d'affiner le niveau de compression. Ce niveau est généralement lié à l'effort qu'ils doivent déployer pour trouver des modèles répétitifs ; plus le niveau de compression est élevé, plus le processus de compression nécessite du temps et de la mémoire.
Bien qu'ils soient livrés avec des valeurs par défaut, ils peuvent être modifiés à l'aide du mécanisme de configuration générique de Parquet, bien qu'en utilisant des clés différentes pour chaque codec.
De plus, les valeurs à choisir ne sont pas standards et dépendent de chaque codec, vous devez donc vous référer à la documentation de chaque algorithme pour comprendre ce que propose chaque niveau.
ZSTD
Pour référencer la configuration au niveau, le codec ZSTD déclare une constante : ZstandardCodec.PARQUET_COMPRESS_ZSTD_LEVEL
.
Les valeurs possibles vont de 1 à 22, la valeur par défaut est 3.
<code class="language-java">CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz) .withCompressionCodec(CompressionCodecName.ZSTD) .build();</code>
LZO
Pour référencer la configuration au niveau, le codec LZO déclare une constante : LzoCodec.LZO_COMPRESSION_LEVEL_KEY
.
Les valeurs possibles vont de 1 à 9, 99 et 999, la valeur par défaut étant « 999 ».
<code class="language-java">ParquetWriter<Organization> writer = AvroParquetWriter.<Organization>builder(outputFile) .withSchema(new Organization().getSchema()) .withCompressionCodec(CompressionCodecName.ZSTD) .build();</code>
GZIP
Il ne déclare aucune constante, vous devez utiliser directement la chaîne "zlib.compress.level", les valeurs possibles vont de 0 à 9, la valeur par défaut est "6".
<code class="language-java">ParquetWriter<Organization> writer = ProtoParquetWriter.<Organization>builder(outputFile) .withMessage(Organization.class) .withCompressionCodec(CompressionCodecName.ZSTD) .build();</code>
Pour analyser les performances de différents algorithmes de compression, j'utiliserai deux ensembles de données publics contenant différents types de données :
Je vais évaluer certains des algorithmes de compression activés dans Parquet Java : UNCOMPRESSED, SNAPPY, GZIP, LZO, ZSTD, LZ4_RAW.
Comme prévu, j'utiliserai Carpet avec la configuration par défaut fournie par parquet-java et le niveau de compression par défaut pour chaque algorithme.
Vous pouvez trouver le code source sur GitHub, les tests ont été effectués sur un ordinateur portable équipé d'un processeur AMD Ryzen 7 4800HS et du JDK 17.
Pour comprendre les performances de chaque compression, nous utiliserons le fichier CSV équivalent comme référence.
格式 | 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 |
Parmi les deux tests, la compression utilisant GZip et Zstandard était la plus efficace.
En utilisant uniquement la technologie d'encodage Parquet, la taille du fichier peut être réduite de 25 à 32 % de la taille CSV d'origine. Avec une compression supplémentaire appliquée, elle sera réduite à 9 % à 15 % de la taille du CSV.
Quelle surcharge la compression des informations entraîne-t-elle ?
Si on écrit trois fois la même information et calcule la moyenne des secondes, on obtient :
算法 | 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 et LZO atteignent des temps similaires sans compression, tandis que ZSTD ajoute une certaine surcharge. GZIP a enregistré les pires performances, avec des temps d'écriture ralentis de 50 %.
La lecture d'un fichier est plus rapide que l'écriture car moins de calculs sont nécessaires.
Le temps en secondes pour lire toutes les colonnes du fichier est :
算法 | 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 |
Le temps de lecture est proche de celui des informations non compressées, et la surcharge de décompression est comprise entre 10 % et 20 %.
Aucun algorithme n'est significativement meilleur que les autres en termes de temps de lecture et d'écriture, tous sont dans une fourchette similaire. Dans la plupart des cas, la compression des informations peut compenser les gains d'espace (et le temps de transmission) perdus.
Dans ces deux cas d'utilisation, le facteur décisif dans le choix de l'un ou l'autre algorithme est probablement le taux de compression obtenu, ZSTD et Gzip étant prédominants (mais les temps d'écriture étant inférieurs).
Chaque algorithme a ses avantages, la meilleure option est donc de le tester avec vos données et de déterminer quel facteur est le plus important :
Comme tout dans la vie, c'est un compromis et il faut voir ce qui le compense le mieux. Dans Carpet, il utilise par défaut Snappy pour la compression si vous ne configurez rien.
La valeur doit être l'une des valeurs disponibles dans l'énumération CompressionCodecName. À chaque valeur d'énumération est associé le nom de la classe qui implémente l'algorithme :
<code class="language-java">CarpetWriter<T> writer = new CarpetWriter.Builder<>(outputFile, clazz) .withCompressionCodec(CompressionCodecName.ZSTD) .build();</code>
Parquet utilisera la réflexion pour instancier la classe spécifiée, qui doit implémenter l'interface CompressionCodec. Si vous regardez son code source, vous verrez qu'il se trouve dans le projet Hadoop, pas dans Parquet. Cela montre à quel point Parquet est bien couplé à Hadoop dans son implémentation Java.
Pour utiliser l'un de ces codecs, vous devez vous assurer d'avoir ajouté le JAR contenant son implémentation en tant que dépendance.
Toutes les implémentations ne sont pas présentes dans les dépendances transitives que vous avez lors de l'ajout de parquet-java, ou vous excluez peut-être les dépendances Hadoop de manière trop agressive.
Dans la dépendance org.apache.parquet:parquet-hadoop, incluez les implémentations de SnappyCodec, ZstandardCodec et Lz4RawCodec, qui importe de manière transitive les dépendances snappy-java, zstd-jni et aircompressor ainsi que les implémentations réelles de ces trois algorithmes .
Dans la dépendance hadoop-common:hadoop-common, contient l'implémentation de GzipCodec.
Où sont les implémentations de BrotliCodec et LzoCodec ? Ils ne font partie d'aucune dépendance Parquet ou Hadoop, donc si vous les utilisez sans ajouter de dépendances supplémentaires, votre application ne pourra pas utiliser de fichiers compressés dans ces formats.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!