Este repositorio contiene la especificación de las definiciones de Apache Parquet y Apache Thrift para leer y escribir metadatos de Parquet.
Apache Parquet es un formato de archivo de datos orientado a columnas de código abierto diseñado para el almacenamiento y la recuperación de datos eficientes. Proporciona esquemas de codificación y compresión de alto rendimiento para manejar datos complejos de forma masiva y es compatible con muchos lenguajes de programación y herramientas de análisis.
Creamos Parquet para que las ventajas de la representación de datos en columnas comprimidas y eficientes estén disponibles para cualquier proyecto en el ecosistema Hadoop.
Parquet se construye desde cero teniendo en mente estructuras de datos anidadas complejas y utiliza el algoritmo de ensamblaje y destrucción de registros descrito en el artículo de Dremel. Creemos que este enfoque es superior al simple aplanamiento de espacios de nombres anidados.
Parquet está diseñado para admitir esquemas de codificación y compresión muy eficientes. Múltiples proyectos han demostrado el impacto en el rendimiento de aplicar el esquema de compresión y codificación correcto a los datos. Parquet permite especificar esquemas de compresión a nivel de columna y está preparado para el futuro para permitir agregar más codificaciones a medida que se inventen e implementen.
El parquet está diseñado para ser utilizado por cualquier persona. El ecosistema de Hadoop es rico en marcos de procesamiento de datos y no estamos interesados en tener favoritos. Creemos que un sustrato de almacenamiento en columnas eficiente y bien implementado debería ser útil para todos los marcos sin el costo de dependencias extensas y difíciles de configurar.
El proyecto parquet-format
contiene especificaciones de formato y definiciones Thrift de metadatos necesarios para leer correctamente los archivos Parquet.
El proyecto parquet-java
contiene múltiples submódulos, que implementan los componentes principales de lectura y escritura de un flujo de datos anidado y orientado a columnas, asignan este núcleo al formato parquet y proporcionan formatos de entrada/salida de Hadoop, cargadores Pig y otros. Utilidades basadas en Java para interactuar con Parquet.
El proyecto parquet-compatibility
contiene pruebas de compatibilidad que se pueden utilizar para verificar que las implementaciones en diferentes idiomas puedan leer y escribir los archivos de cada uno.
Los recursos de Java se pueden crear utilizando mvn package
. La versión estable actual siempre debería estar disponible en Maven Central.
Los recursos de ahorro de C++ se pueden generar mediante make.
Thrift también se puede generar mediante código en cualquier otro lenguaje compatible con Thrift.
Bloque (bloque HDFS): esto significa un bloque en HDFS y el significado no cambia para describir este formato de archivo. El formato de archivo está diseñado para funcionar bien sobre HDFS.
Archivo: un archivo HDFS que debe incluir los metadatos del archivo. En realidad, no es necesario que contenga los datos.
Grupo de filas: una partición horizontal lógica de los datos en filas. No existe una estructura física garantizada para un grupo de filas. Un grupo de filas consta de un fragmento de columna para cada columna del conjunto de datos.
Fragmento de columna: fragmento de datos de una columna en particular. Viven en un grupo de filas particular y se garantiza que serán contiguos en el archivo.
Página: los fragmentos de columnas se dividen en páginas. Una página es conceptualmente una unidad indivisible (en términos de compresión y codificación). Puede haber varios tipos de páginas intercaladas en un fragmento de columna.
Jerárquicamente, un archivo consta de uno o más grupos de filas. Un grupo de filas contiene exactamente un fragmento de columna por columna. Los fragmentos de columna contienen una o más páginas.
Este archivo y la definición de Thrift deben leerse juntos para comprender el formato.
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"
En el ejemplo anterior, hay N columnas en esta tabla, divididas en M grupos de filas. Los metadatos del archivo contienen las ubicaciones de todas las ubicaciones de inicio de los fragmentos de columna. Se pueden encontrar más detalles sobre lo que contienen los metadatos en la definición de Thrift.
Los metadatos del archivo se escriben después de los datos para permitir la escritura en una sola pasada.
Se espera que los lectores lean primero los metadatos del archivo para encontrar todos los fragmentos de columnas que les interesan. Luego, los fragmentos de columnas deben leerse secuencialmente.
Hay dos tipos de metadatos: metadatos de archivo y metadatos de encabezado de página. Todas las estructuras de ahorro se serializan mediante TCompactProtocol.
Los tipos admitidos por el formato de archivo pretenden ser lo más mínimos posible, centrándose en cómo afectan los tipos al almacenamiento en disco. Por ejemplo, los enteros de 16 bits no se admiten explícitamente en el formato de almacenamiento, ya que están cubiertos por enteros de 32 bits con una codificación eficiente. Esto reduce la complejidad de implementar lectores y escritores para el formato. Los tipos son:
Los tipos lógicos se utilizan para ampliar los tipos que el parquet puede almacenar, especificando cómo se deben interpretar los tipos primitivos. Esto mantiene el conjunto de tipos primitivos al mínimo y reutiliza las codificaciones eficientes de parquet. Por ejemplo, las cadenas se almacenan con el tipo primitivo BYTE_ARRAY con una anotación STRING. Estas anotaciones definen cómo decodificar e interpretar aún más los datos. Las anotaciones se almacenan como campos LogicalType
en los metadatos del archivo y se documentan en LogicalTypes.md.
Parquet almacena estadísticas mínimas y máximas en varios niveles (como fragmento de columna, índice de columnas y página de datos). La comparación de valores de un tipo obedece las siguientes reglas:
Cada tipo lógico tiene un orden de comparación específico. Si una columna está anotada con un tipo lógico desconocido, es posible que no se utilicen estadísticas para podar datos. El orden de clasificación de los tipos lógicos está documentado en la página LogicalTypes.md.
Para tipos primitivos, se aplican las siguientes reglas:
BOOLEAN - falso, verdadero
INT32, INT64: comparación firmada.
FLOAT, DOUBLE: comparación firmada con manejo especial de NaN y ceros firmados. Los detalles están documentados en la definición de Thrift en la unión ColumnOrder
. Se resumen aquí, pero la definición de Ahorro se considera autorizada:
+0.0
en el campo de estadísticas máximas.-0.0
en el campo de estadísticas mínimas.Para compatibilidad con versiones anteriores al leer archivos:
BYTE_ARRAY y FIXED_LEN_BYTE_ARRAY: comparación lexicográfica de bytes sin signo.
Para codificar columnas anidadas, Parquet utiliza la codificación Dremel con niveles de definición y repetición. Los niveles de definición especifican cuántos campos opcionales se definen en la ruta de la columna. Los niveles de repetición especifican en qué campo repetido de la ruta se repite el valor. Los niveles máximos de definición y repetición se pueden calcular a partir del esquema (es decir, cuánto anidamiento hay). Esto define el número máximo de bits necesarios para almacenar los niveles (los niveles se definen para todos los valores de la columna).
Se admiten dos codificaciones para los niveles BIT_PACKED y RLE. Ahora solo se utiliza RLE, ya que reemplaza a BIT_PACKED.
La nulidad está codificada en los niveles de definición (que está codificada en longitud de ejecución). Los valores NULL no están codificados en los datos. Por ejemplo, en un esquema no anidado, una columna con 1000 NULL se codificaría con codificación de longitud de ejecución (0, 1000 veces) para los niveles de definición y nada más.
Para las páginas de datos, los 3 fragmentos de información se codifican uno detrás del otro, después del encabezado de la página. No se permite relleno en la página de datos. En orden tenemos:
El valor de uncompressed_page_size
especificado en el encabezado es para las 3 piezas combinadas.
Los valores codificados para la página de datos siempre son necesarios. Los niveles de definición y repetición son opcionales, según la definición del esquema. Si la columna no está anidada (es decir, la ruta a la columna tiene longitud 1), no codificamos los niveles de repetición (siempre tendría el valor 1). Para los datos que se requieren, se omiten los niveles de definición (si están codificados, siempre tendrán el valor del nivel máximo de definición).
Por ejemplo, en el caso de que la columna no esté anidada y sea obligatoria, los datos de la página son solo los valores codificados.
Las codificaciones admitidas se describen en Encodings.md
Los códecs de compresión admitidos se describen en Compression.md
Los fragmentos de columnas se componen de páginas escritas una al lado de la otra. Las páginas comparten un encabezado común y los lectores pueden omitir las páginas que no les interesan. Los datos de la página siguen al encabezado y se pueden comprimir y/o codificar. La compresión y codificación se especifican en los metadatos de la página.
Un fragmento de columna puede estar parcial o completamente codificado en diccionario. Significa que los índices del diccionario se guardan en las páginas de datos en lugar de los valores reales. Los valores reales se almacenan en la página del diccionario. Consulte los detalles en Codificaciones.md. La página del diccionario debe colocarse en la primera posición del fragmento de columna. Como máximo se puede colocar una página de diccionario en un fragmento de columna.
Además, los archivos pueden contener un índice de columna opcional para permitir a los lectores omitir páginas de manera más eficiente. Consulte PageIndex.md para obtener detalles y el razonamiento detrás de agregarlos al formato.
Se pueden realizar sumas de verificación individuales de páginas de todo tipo. Esto permite deshabilitar las sumas de verificación a nivel de archivo HDFS para admitir mejor las búsquedas de una sola fila. Las sumas de comprobación se calculan utilizando el algoritmo CRC32 estándar, como se utiliza, por ejemplo, en GZip, en la representación binaria serializada de una página (sin incluir el encabezado de la página).
Si los metadatos del archivo están dañados, el archivo se pierde. Si los metadatos de la columna están dañados, ese fragmento de columna se pierde (pero los fragmentos de columna para esta columna en otros grupos de filas están bien). Si el encabezado de una página está dañado, las páginas restantes de ese fragmento se pierden. Si los datos dentro de una página están corruptos, esa página se pierde. El archivo será más resistente a la corrupción con grupos de filas más pequeños.
Posible extensión: con grupos de filas más pequeños, el mayor problema es colocar los metadatos del archivo al final. Si ocurre un error al escribir los metadatos del archivo, todos los datos escritos serán ilegibles. Esto se puede solucionar escribiendo los metadatos del archivo en cada enésimo grupo de filas. Los metadatos de cada archivo serían acumulativos e incluirían todos los grupos de filas escritos hasta el momento. Combinando esto con la estrategia utilizada para archivos rc o avro usando marcadores de sincronización, un lector podría recuperar archivos parcialmente escritos.
El formato está diseñado explícitamente para separar los metadatos de los datos. Esto permite dividir columnas en varios archivos, así como hacer que un único archivo de metadatos haga referencia a varios archivos de parquet.
Hay muchos lugares en el formato para extensiones compatibles:
Parquet Thrift IDL reserva el ID de campo 32767
de cada estructura Thrift para extensiones. El tipo (Ahorro) de este campo es siempre binary
.
Apache/parquet-testing contiene un conjunto de archivos Parquet para fines de prueba.
Comente sobre el tema y/o comuníquese con la lista de correo de parquet-dev con sus preguntas e ideas. Los cambios a esta definición de formato central se proponen y discuten en profundidad en la lista de correo. También puede estar interesado en contribuir al subproyecto Parquet-Java, que contiene todas las API y la implementación del lado Java. Consulte la sección "Cómo contribuir" del proyecto Parquet-Java.
Nos obligamos a nosotros mismos y a la comunidad de desarrolladores de Parquet a cumplir un código de conducta según lo descrito por Twitter OSS: https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md.
Copyright 2013 Twitter, Cloudera y otros contribuyentes.
Licenciado bajo la Licencia Apache, Versión 2.0: http://www.apache.org/licenses/LICENSE-2.0