Ce référentiel contient la spécification des définitions Apache Parquet et Apache Thrift pour lire et écrire les métadonnées Parquet.
Apache Parquet est un format de fichier de données open source orienté colonnes, conçu pour un stockage et une récupération efficaces des données. Il fournit des schémas de compression et de codage hautes performances pour gérer des données complexes en masse et est pris en charge dans de nombreux langages de programmation et outils d'analyse.
Nous avons créé Parquet pour mettre les avantages d'une représentation de données en colonnes compressée et efficace à la disposition de tout projet de l'écosystème Hadoop.
Parquet est construit à partir de zéro avec des structures de données imbriquées complexes à l'esprit et utilise l'algorithme de déchiquetage et d'assemblage d'enregistrements décrit dans l'article de Dremel. Nous pensons que cette approche est supérieure au simple aplatissement des espaces de noms imbriqués.
Parquet est conçu pour prendre en charge des schémas de compression et d'encodage très efficaces. Plusieurs projets ont démontré l’impact sur les performances de l’application du bon schéma de compression et de codage aux données. Parquet permet de spécifier des schémas de compression au niveau de chaque colonne et est évolutif pour permettre l'ajout de plus d'encodages au fur et à mesure de leur invention et de leur mise en œuvre.
Le parquet est conçu pour être utilisé par tout le monde. L'écosystème Hadoop est riche en frameworks de traitement de données, et nous ne souhaitons pas jouer aux favoris. Nous pensons qu'un substrat de stockage en colonnes efficace et bien implémenté devrait être utile à tous les frameworks sans le coût de dépendances étendues et difficiles à configurer.
Le projet parquet-format
contient des spécifications de format et des définitions Thrift des métadonnées requises pour lire correctement les fichiers Parquet.
Le projet parquet-java
contient plusieurs sous-modules, qui implémentent les composants de base de lecture et d'écriture d'un flux de données imbriqué et orienté colonnes, mappent ce noyau sur le format parquet et fournissent des formats d'entrée/sortie Hadoop, des chargeurs Pig et d'autres utilitaires basés sur Java pour interagir avec Parquet.
Le projet parquet-compatibility
contient des tests de compatibilité qui peuvent être utilisés pour vérifier que les implémentations dans différentes langues peuvent lire et écrire les fichiers les unes des autres.
Les ressources Java peuvent être créées à l'aide mvn package
. La version stable actuelle devrait toujours être disponible sur Maven Central.
Les ressources d'épargne C++ peuvent être générées via make.
Thrift peut également être généré par code dans n’importe quel autre langage pris en charge par Thrift.
Bloc (bloc HDFS) : Cela signifie un bloc dans HDFS et la signification est inchangée pour décrire ce format de fichier. Le format de fichier est conçu pour fonctionner correctement avec HDFS.
Fichier : un fichier HDFS qui doit inclure les métadonnées du fichier. Il n'est pas nécessaire qu'il contienne réellement les données.
Groupe de lignes : partitionnement horizontal logique des données en lignes. Aucune structure physique n’est garantie pour un groupe de lignes. Un groupe de lignes se compose d'un bloc de colonnes pour chaque colonne de l'ensemble de données.
Morceau de colonne : un morceau de données pour une colonne particulière. Ils vivent dans un groupe de lignes particulier et sont garantis contigus dans le fichier.
Page : les blocs de colonnes sont divisés en pages. Une page est conceptuellement une unité indivisible (en termes de compression et d'encodage). Il peut y avoir plusieurs types de pages entrelacées dans un bloc de colonnes.
Hiérarchiquement, un fichier se compose d'un ou plusieurs groupes de lignes. Un groupe de lignes contient exactement un fragment de colonne par colonne. Les blocs de colonnes contiennent une ou plusieurs pages.
Ce fichier et la définition de Thrift doivent être lus ensemble pour comprendre le format.
4-byte magic number "PAR1"
<Column 1 Chunk 1>
<Column 2 Chunk 1>
...
<Column N Chunk 1>
<Column 1 Chunk 2>
<Column 2 Chunk 2>
...
<Column N Chunk 2>
...
<Column 1 Chunk M>
<Column 2 Chunk M>
...
<Column N Chunk M>
File Metadata
4-byte length in bytes of file metadata (little endian)
4-byte magic number "PAR1"
Dans l'exemple ci-dessus, il y a N colonnes dans ce tableau, divisées en M groupes de lignes. Les métadonnées du fichier contiennent les emplacements de tous les emplacements de début des fragments de colonnes. Plus de détails sur le contenu des métadonnées peuvent être trouvés dans la définition de Thrift.
Les métadonnées du fichier sont écrites après les données pour permettre une écriture en un seul passage.
Les lecteurs doivent d’abord lire les métadonnées du fichier pour trouver tous les fragments de colonnes qui les intéressent. Les fragments de colonnes doivent ensuite être lus séquentiellement.
Il existe deux types de métadonnées : les métadonnées de fichier et les métadonnées d'en-tête de page. Toutes les structures d'épargne sont sérialisées à l'aide du TCompactProtocol.
Les types pris en charge par le format de fichier sont destinés à être aussi minimes que possible, en mettant l'accent sur la façon dont les types affectent le stockage sur disque. Par exemple, les entiers 16 bits ne sont pas explicitement pris en charge dans le format de stockage car ils sont couverts par des entiers 32 bits avec un encodage efficace. Cela réduit la complexité de la mise en œuvre des lecteurs et des rédacteurs pour le format. Les types sont :
Les types logiques sont utilisés pour étendre les types que parquet peut être utilisé pour stocker, en spécifiant comment les types primitifs doivent être interprétés. Cela réduit au minimum l'ensemble des types primitifs et réutilise les encodages efficaces de Parquet. Par exemple, les chaînes sont stockées avec le type primitif BYTE_ARRAY avec une annotation STRING. Ces annotations définissent comment décoder et interpréter davantage les données. Les annotations sont stockées sous forme de champs LogicalType
dans les métadonnées du fichier et sont documentées dans LogicalTypes.md.
Parquet stocke les statistiques min/max à plusieurs niveaux (tels que Column Chunk, Column Index et Data Page). La comparaison des valeurs d'un type obéit aux règles suivantes :
Chaque type logique a un ordre de comparaison spécifié. Si une colonne est annotée avec un type logique inconnu, les statistiques ne peuvent pas être utilisées pour élaguer les données. L'ordre de tri des types logiques est documenté dans la page LogicalTypes.md.
Pour les types primitifs, les règles suivantes s'appliquent :
BOOLÉEN - faux, vrai
INT32, INT64 - Comparaison signée.
FLOAT, DOUBLE - Comparaison signée avec gestion spéciale des NaN et des zéros signés. Les détails sont documentés dans la définition Thrift dans l’union ColumnOrder
. Ils sont résumés ici mais la définition de Thrift fait autorité :
+0.0
doit être écrit dans le champ des statistiques maximales.-0.0
doit être écrit dans le champ des statistiques minimales.Pour une compatibilité ascendante lors de la lecture de fichiers :
BYTE_ARRAY et FIXED_LEN_BYTE_ARRAY - Comparaison lexicographique non signée par octet.
Pour encoder les colonnes imbriquées, Parquet utilise l'encodage Dremel avec des niveaux de définition et de répétition. Les niveaux de définition spécifient le nombre de champs facultatifs définis dans le chemin de la colonne. Les niveaux de répétition spécifient à quel champ répété dans le chemin la valeur est répétée. Les niveaux maximum de définition et de répétition peuvent être calculés à partir du schéma (c'est-à-dire le nombre d'imbrications). Ceci définit le nombre maximum de bits requis pour stocker les niveaux (les niveaux sont définis pour toutes les valeurs de la colonne).
Deux encodages pour les niveaux sont pris en charge BIT_PACKED et RLE. Seul RLE est désormais utilisé car il remplace BIT_PACKED.
La nullité est codée dans les niveaux de définition (qui sont codés en longueur). Les valeurs NULL ne sont pas codées dans les données. Par exemple, dans un schéma non imbriqué, une colonne contenant 1 000 valeurs NULL serait codée avec un codage de longueur d'exécution (0, 1 000 fois) pour les niveaux de définition et rien d'autre.
Pour les pages de données, les 3 informations sont encodées dos à dos, après l'en-tête de la page. Aucun remplissage n'est autorisé dans la page de données. Dans l'ordre nous avons :
La valeur de uncompressed_page_size
spécifiée dans l'en-tête concerne les 3 éléments combinés.
Les valeurs codées pour la page de données sont toujours requises. Les niveaux de définition et de répétition sont facultatifs, basés sur la définition du schéma. Si la colonne n'est pas imbriquée (c'est-à-dire que le chemin vers la colonne a une longueur de 1), nous ne codons pas les niveaux de répétition (il aura toujours la valeur 1). Pour les données requises, les niveaux de définition sont ignorés (si elles sont codées, elles auront toujours la valeur du niveau de définition maximum).
Par exemple, dans le cas où la colonne est non imbriquée et obligatoire, les données de la page sont uniquement les valeurs codées.
Les encodages pris en charge sont décrits dans Encodings.md
Les codecs de compression pris en charge sont décrits dans Compression.md
Les blocs de colonnes sont composés de pages écrites dos à dos. Les pages partagent un en-tête commun et les lecteurs peuvent sauter les pages qui ne les intéressent pas. Les données de la page suivent l'en-tête et peuvent être compressées et/ou codées. La compression et l'encodage sont spécifiés dans les métadonnées de la page.
Un morceau de colonne peut être partiellement ou entièrement codé dans un dictionnaire. Cela signifie que les index du dictionnaire sont enregistrés dans les pages de données au lieu des valeurs réelles. Les valeurs réelles sont stockées dans la page du dictionnaire. Voir les détails dans Encodings.md. La page du dictionnaire doit être placée à la première position du bloc de colonne. Au plus une page de dictionnaire peut être placée dans un bloc de colonnes.
De plus, les fichiers peuvent contenir un index de colonne facultatif pour permettre aux lecteurs de sauter des pages plus efficacement. Voir PageIndex.md pour plus de détails et le raisonnement derrière leur ajout au format.
Les pages de toutes sortes peuvent être additionnées individuellement. Cela permet de désactiver les sommes de contrôle au niveau du fichier HDFS, pour mieux prendre en charge les recherches sur une seule ligne. Les sommes de contrôle sont calculées à l'aide de l'algorithme standard CRC32 - tel qu'utilisé par exemple dans GZip - sur la représentation binaire sérialisée d'une page (sans compter l'en-tête de la page lui-même).
Si les métadonnées du fichier sont corrompues, le fichier est perdu. Si les métadonnées de la colonne sont corrompues, ce fragment de colonne est perdu (mais les fragments de colonne pour cette colonne dans d'autres groupes de lignes sont corrects). Si un en-tête de page est corrompu, les pages restantes de ce bloc sont perdues. Si les données d'une page sont corrompues, cette page est perdue. Le fichier sera plus résistant à la corruption avec des groupes de lignes plus petits.
Extension potentielle : avec des groupes de lignes plus petits, le plus gros problème est de placer les métadonnées du fichier à la fin. Si une erreur se produit lors de l'écriture des métadonnées du fichier, toutes les données écrites seront illisibles. Cela peut être résolu en écrivant les métadonnées du fichier tous les Nièmes groupes de lignes. Chaque métadonnée de fichier serait cumulative et inclurait tous les groupes de lignes écrits jusqu'à présent. En combinant cela avec la stratégie utilisée pour les fichiers rc ou avro utilisant des marqueurs de synchronisation, un lecteur pourrait récupérer des fichiers partiellement écrits.
Le format est explicitement conçu pour séparer les métadonnées des données. Cela permet de diviser les colonnes en plusieurs fichiers, ainsi que d'avoir un seul fichier de métadonnées faisant référence à plusieurs fichiers Parquet.
Il existe de nombreux endroits dans le format pour les extensions compatibles :
Parquet Thrift IDL réserve l'ID de champ 32767
de chaque structure Thrift pour les extensions. Le type (Thrift) de ce champ est toujours binary
.
Apache/parquet-testing contient un ensemble de fichiers Parquet à des fins de test.
Commentez le problème et/ou contactez la liste de diffusion parquet-dev avec vos questions et idées. Les modifications apportées à cette définition de format de base sont proposées et discutées en profondeur sur la liste de diffusion. Vous pourriez également être intéressé à contribuer au sous-projet Parquet-Java, qui contient toute l'implémentation et les API côté Java. Voir la section "Comment contribuer" du projet Parquet-Java
Nous nous soumettons, ainsi que la communauté des développeurs Parquet, à un code de conduite tel que décrit par Twitter OSS : https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md.
Copyright 2013 Twitter, Cloudera et autres contributeurs.
Sous licence Apache, version 2.0 : http://www.apache.org/licenses/LICENSE-2.0