Inmersión en Delta Lake: aplicación y evolución del esquema

Hola Habr! Les presento la traducción del artículo “Buceando en el lago Delta: cumplimiento y evolución del esquema” por Burak Yavuz, Brenner Heintz y Denny Lee, que se preparó antes del inicio del curso de Ingeniero de datos de OTUS.





Los datos, como nuestra experiencia, se están acumulando y desarrollando constantemente. Para mantenerse al día, nuestros modelos mentales del mundo deben adaptarse a nuevos datos, algunos de los cuales contienen nuevas dimensiones, nuevas formas de observar cosas de las que no teníamos idea antes. Estos modelos mentales no son muy diferentes de los esquemas de tablas que determinan cómo clasificamos y procesamos la nueva información.

Esto nos lleva a la cuestión de la gestión de circuitos. A medida que las tareas y los requisitos comerciales cambian con el tiempo, la estructura de sus datos cambia. Delta Lake facilita la implementación de nuevas mediciones cuando los datos cambian. Los usuarios tienen acceso a una semántica simple para administrar los diseños de sus tablas. Estas herramientas incluyen Schema Enforcement, que protege a los usuarios de obstruir inadvertidamente sus tablas con errores o datos innecesarios, así como Schema Evolution, que agrega automáticamente nuevas columnas con datos valiosos a los lugares apropiados. En este artículo, profundizaremos en el uso de estas herramientas.

Comprender los esquemas de tabla


Cada DataFrame en Apache Spark contiene un esquema que define un formulario de datos, como tipos de datos, columnas y metadatos. Con Delta Lake, el esquema de la tabla se guarda en formato JSON dentro del registro de transacciones.

¿Qué es hacer cumplir el esquema?


Schema Enforcement, también conocido como Schema Validation, es un mecanismo de defensa en Delta Lake que garantiza la calidad de los datos al rechazar registros que no coinciden con el esquema de la tabla. Al igual que la anfitriona en la recepción de un restaurante popular, que acepta solo con reserva previa, verifica si cada columna de datos ingresada en la tabla está en la lista correspondiente de columnas esperadas (en otras palabras, ¿hay una "reserva" para cada una de ellas), y rechaza cualquier entrada con columnas que no estén en la lista.

¿Cómo funciona hacer cumplir un circuito?


Delta Lake utiliza una verificación de esquema al escribir, lo que significa que todos los registros nuevos de la tabla se verifican para verificar su compatibilidad con el esquema de la tabla de destino durante la grabación. Si el esquema es incompatible, Delta Lake cancela completamente la transacción (los datos no se escriben) y lanza una excepción para informar al usuario sobre la discrepancia.
Para determinar la compatibilidad de registros con una tabla, Delta Lake utiliza las siguientes reglas. Marco de datos grabable:

  • no puede contener columnas adicionales que no están en el esquema de la tabla de destino. Y viceversa, todo está en orden si los datos de entrada no contienen absolutamente todas las columnas de la tabla; a estas columnas simplemente se les asignarán valores cero.
  • , . StringType, DataFrame IntegerType, .
  • no puede contener nombres de columna que difieran solo en mayúsculas y minúsculas. Esto significa que no puede tener columnas con los nombres 'Foo' y 'foo' definidos en la misma tabla. Aunque Spark se puede usar en mayúsculas o minúsculas (predeterminado), Delta Lake es sensible a mayúsculas y minúsculas. El parquet distingue entre mayúsculas y minúsculas al almacenar y devolver información de columna. Para evitar posibles errores, corrupción de datos o pérdida de datos (que encontramos personalmente en los Databricks), decidimos agregar esta restricción.

Para ilustrar esto, echemos un vistazo a lo que sucede en el siguiente código al intentar agregar algunas columnas generadas recientemente a una tabla de Delta Lake que aún no está configurada para aceptarlas.

#  DataFrame ,       Delta Lake
loans = sql("""
            SELECT addr_state, CAST(rand(10)*count as bigint) AS count,
            CAST(rand(10) * 10000 * count AS double) AS amount
            FROM loan_by_state_delta
            """)

#    DataFrame
original_loans.printSchema()

root
  |-- addr_state: string (nullable = true)
  |-- count: integer (nullable = true)
 
#    DataFrame
loans.printSchema()
 
root
  |-- addr_state: string (nullable = true)
  |-- count: integer (nullable = true)
  |-- amount: double (nullable = true) # new column
 
#    DataFrame (  )   
loans.write.format("delta") \
           .mode("append") \
           .save(DELTALAKE_PATH)

Returns:

A schema mismatch detected when writing to the Delta table.
 
To enable schema migration, please set:
'.option("mergeSchema", "true")\'
 
Table schema:
root
-- addr_state: string (nullable = true)
-- count: long (nullable = true)
 
Data schema:
root
-- addr_state: string (nullable = true)
-- count: long (nullable = true)
-- amount: double (nullable = true)
 
If Table ACLs are enabled, these options will be ignored. Please use the ALTER TABLE command for changing the schema.

En lugar de agregar automáticamente nuevas columnas, Delta Lake impone un gráfico y detiene la grabación. Para ayudar a determinar qué columna (o una pluralidad de ellas) es la causa de la falta de coincidencia, Spark extrae ambos esquemas de la pila de la pila para compararlos.

¿De qué sirve hacer cumplir un esquema?


Dado que aplicar un esquema es una prueba bastante rigurosa, es una herramienta excelente para usar un conjunto de datos limpio y totalmente transformado que está listo para la producción o el consumo como guardián. Como regla general, se aplica a las tablas que proporcionan datos directamente:

  • Algoritmos de aprendizaje automático
  • Paneles de BI
  • Análisis de datos y herramientas de visualización
  • Cualquier sistema de producción que requiera esquemas semánticos estrictamente estructurados y fuertemente tipados.

Para preparar sus datos para esta barrera final, muchos usuarios usan una arquitectura simple de "salto múltiple", que gradualmente trae estructura a sus tablas. Para obtener más información sobre esto, puede consultar el artículo de Aprendizaje automático a nivel de máquina de Delta Lake.

Por supuesto, puede usar la aplicación forzada del esquema en cualquier parte de su canalización, pero recuerde que la transmisión a una tabla en este caso puede ser frustrante, porque, por ejemplo, olvidó que agregó otra columna a los datos de entrada.

Prevención de reducción de datos


En este punto, puede que se pregunte por qué tanto revuelo. Después de todo, a veces un error inesperado de "desajuste de esquema" puede configurarlo en su flujo de trabajo, especialmente si es nuevo en Delta Lake. ¿Por qué no dejar que el esquema cambie según sea necesario para que yo pueda escribir mi DataFrame, pase lo que pase?

Como dice el viejo dicho, "una onza de prevención vale una libra de cura". En algún momento, si no se ocupa de aplicar su esquema, los problemas de compatibilidad de tipos de datos levantarán sus cabezas desagradables: a primera vista, las fuentes homogéneas de datos en bruto pueden contener casos límite, columnas dañadas, mapeos malformados u otras cosas aterradoras que sueñan en pesadillas El mejor enfoque es detener a estos enemigos en las puertas, aplicando el esquema, y ​​lidiar con ellos a la luz, no más tarde, cuando comiencen a explorar las profundidades oscuras de su código de trabajo.

Hacer cumplir un esquema le da la confianza de que el esquema de su tabla no cambiará, a menos que usted mismo confirme la opción de cambio. Esto evita la dilución de los datos, que puede ocurrir cuando se agregan nuevas columnas con tanta frecuencia que las tablas comprimidas previamente valiosas pierden su valor y utilidad debido a una avalancha de datos. Animándote a ser intencional, establecer altos estándares y esperar alta calidad, hacer cumplir el esquema hace exactamente lo que se pretendía para ayudarte a ser honesto y mantener tus mesas limpias.

Si, después de una consideración adicional, decide que realmente necesita agregar una nueva columna, no hay problema, a continuación encontrará una solución de una sola línea. ¡La solución es la evolución del circuito!

¿Qué es la evolución del circuito?


La evolución del esquema es una característica que permite a los usuarios cambiar fácilmente el esquema de la tabla actual de acuerdo con los datos que cambian con el tiempo. Con mayor frecuencia, se usa cuando se realiza una operación de agregar o reescribir para adaptar automáticamente el diseño para incluir una o más columnas nuevas.

¿Cómo funciona la evolución del circuito?


Siguiendo el ejemplo de la sección anterior, los desarrolladores pueden usar fácilmente la evolución del esquema para agregar nuevas columnas que fueron rechazadas previamente debido a la falta de coincidencia del esquema. La evolución del esquema se activa agregando .option('mergeSchema', 'true')a su equipo Spark..write .writeStream.

#   mergeSchema
loans.write.format("delta") \
           .option("mergeSchema", "true") \
           .mode("append") \
           .save(DELTALAKE_SILVER_PATH)

Para ver el gráfico, ejecute la siguiente consulta Spark SQL

#     ,  ,    
%sql
SELECT addr_state, sum(`amount`) AS amount
FROM loan_by_state_delta
GROUP BY addr_state
ORDER BY sum(`amount`)
DESC LIMIT 10

imagen
Alternativamente, puede configurar esta opción para toda la sesión de spark.databricks.delta.schema.autoMerge = TrueSpark agregando Spark a la configuración. Pero use esto con precaución, porque aplicar un esquema ya no le advertirá sobre inconsistencias inadvertidas con el esquema.

Al incluir un parámetro en la consulta mergeSchema, todas las columnas que están presentes en el DataFrame pero que no están en la tabla de destino se agregan automáticamente al final del esquema como parte de la transacción de escritura. Los campos anidados también se pueden agregar, y también se agregarán al final de las columnas de estructura correspondientes.

Los ingenieros y científicos de fechas pueden usar esta opción para agregar nuevas columnas (tal vez una métrica rastreada recientemente o una columna de métricas de ventas este mes) a sus tablas de producción de aprendizaje automático existentes sin romper los modelos existentes basados ​​en columnas antiguas.

Los siguientes tipos de cambios de esquema están permitidos como parte de la evolución del esquema al agregar o reescribir una tabla:

  • Agregar nuevas columnas (este es el escenario más común)
  • Cambie los tipos de datos de NullType -> cualquier otro tipo o aumente de ByteType -> ShortType -> IntegerType

Otros cambios que son inaceptables como parte de la evolución de un esquema requieren que el esquema y los datos se sobrescriban agregando .option("overwriteSchema", "true"). Por ejemplo, en el caso donde la columna "Foo" era originalmente un número entero, y el nuevo esquema sería un tipo de datos de cadena, entonces todos los archivos Parquet (datos) tendrían que reescribirse. Estos cambios incluyen:

  • eliminar columna
  • cambiar el tipo de datos de una columna existente (en su lugar)
  • renombrar columnas que solo distinguen entre mayúsculas y minúsculas (por ejemplo, "Foo" y "foo")

Finalmente, con la próxima versión de Spark 3.0, DDL explícito (usando ALTER TABLE) será totalmente compatible, lo que permitirá a los usuarios realizar las siguientes acciones en los esquemas de tabla:

  • agregando columnas
  • cambiar comentarios de columna
  • establecer las propiedades de la tabla que determinan el comportamiento de la tabla, por ejemplo, establecer la duración del almacenamiento del registro de transacciones.

¿De qué sirve la evolución del circuito?


Siempre puede usar la evolución del esquema cuando tiene la intención de cambiar el esquema de su tabla (a diferencia de los casos en los que accidentalmente agregó columnas a su DataFrame que no deberían estar allí). Esta es la forma más fácil de migrar su esquema porque agrega automáticamente los nombres de columna y los tipos de datos correctos sin la necesidad de declararlos explícitamente.

Conclusión


Forzar un esquema rechaza cualquier columna nueva u otros cambios de esquema que no sean compatibles con su tabla. Al establecer y mantener estos altos estándares, los analistas e ingenieros pueden confiar en el hecho de que sus datos tienen el más alto nivel de integridad, razonando de manera clara y clara, lo que les permite tomar decisiones comerciales más efectivas.

Por otro lado, la evolución del circuito complementa la aplicación, simplificando los supuestos cambios automáticos en el circuito. Al final, no debería ser difícil agregar una columna.

La aplicación forzada del circuito es yang, donde la evolución del circuito es yin. Cuando se usan juntas, estas características hacen que la reducción de ruido y el ajuste de la señal sean más fáciles que nunca.

También nos gustaría agradecer a Mukul Murti y Pranava Ananda por sus contribuciones a este artículo.

Otros artículos de la serie:

Sumérgete en Delta Lake: desempacando un registro de transacciones



Artículos relacionados


Delta Lake Nivel de producción Aprendizaje automático

¿Qué es un lago de datos?



Aprende más sobre el curso



All Articles