Herramienta de creación de datos o lo que es común entre un Data Warehouse y un Smoothie


¿Cuáles son los principios de un Data Warehouse ideal?

Concéntrese en el valor comercial y el análisis en ausencia de código repetitivo. Administrar DWH como una base de código: control de versiones, revisión, pruebas automatizadas y CI. Modularidad, extensibilidad, código abierto y comunidad. Documentación de usuario amigable y visualización de dependencias (Linaje de datos).

Más información sobre todo esto y sobre el papel de DBT en el ecosistema Big Data & Analytics: bienvenido a cat.

Hola a todos


En contacto con Artemy Kozyr. Durante más de 5 años he trabajado con almacenes de datos, construyendo ETL / ELT, así como análisis y visualización de datos. Actualmente trabajo en Wheely , enseño en OTUS en el curso de Ingeniero de Datos , y hoy quiero compartir con ustedes un artículo que escribí la víspera del inicio de una nueva inscripción en el curso .

Breve reseña


El marco DBT tiene que ver con la letra T en el acrónimo ELT (Extraer - Transformar - Cargar).

Con la llegada de bases de datos analíticas tan productivas y escalables como BigQuery, Redshift, Snowflake, desapareció cualquier sentido para hacer transformaciones fuera del Data Warehouse. 

DBT no descarga datos de las fuentes, pero brinda enormes oportunidades para trabajar con datos que ya están cargados en el almacenamiento (en almacenamiento interno o externo).


El propósito principal de DBT es tomar el código, compilarlo en SQL, ejecutar los comandos en la secuencia correcta en el Repositorio.

Estructura del proyecto DBT


El proyecto consta de directorios y archivos de solo 2 tipos:

  • Modelo (.sql): unidad de transformación expresada por una consulta SELECT
  • Archivo de configuración (.yml): parámetros, configuraciones, pruebas, documentación

En un nivel básico, el trabajo se estructura de la siguiente manera:

  • El usuario prepara el código del modelo en cualquier IDE conveniente
  • Usando la CLI, se lanzan los modelos, DBT compila el código del modelo en SQL
  • El código SQL compilado se ejecuta en el Almacén en la secuencia especificada (gráfico)

Así es como se vería el lanzamiento desde la CLI:


Todo es SELECCIONAR


Esta es una característica excelente del marco de Data Build Tool. En otras palabras, DBT abstrae todo el código relacionado con la materialización de sus consultas en el Almacén (variaciones de los comandos CREAR, INSERTAR, ACTUALIZAR, ELIMINAR ALTERAR, OTORGAR, ...).

Cualquier modelo implica escribir una consulta SELECT, que define el conjunto de datos resultante.

Al mismo tiempo, la lógica de transformación puede ser multinivel y consolidar datos de varios otros modelos. Un ejemplo de un modelo que creará un escaparate de pedidos (f_orders):

{% set payment_methods = ['credit_card', 'coupon', 'bank_transfer', 'gift_card'] %}
 
with orders as (
 
   select * from {{ ref('stg_orders') }}
 
),
 
order_payments as (
 
   select * from {{ ref('order_payments') }}
 
),
 
final as (
 
   select
       orders.order_id,
       orders.customer_id,
       orders.order_date,
       orders.status,
       {% for payment_method in payment_methods -%}
       order_payments.{{payment_method}}_amount,
       {% endfor -%}
       order_payments.total_amount as amount
   from orders
       left join order_payments using (order_id)
 
)
 
select * from final

¿Qué cosas interesantes podemos ver aquí?

Primero: CTE (Expresiones de tabla comunes) se utilizan para organizar y comprender el código que contiene muchas transformaciones y lógica de negocios

. Segundo: El código del modelo es una mezcla de SQL y el lenguaje Jinja (lenguaje de plantillas).

En el ejemplo, el bucle for se utilizó para formar el importe de cada método de pago especificado en la expresión establecida . La función ref también se usa : la capacidad de referirse dentro del código a otros modelos:

  • En el momento de la compilación, ref se convertirá en un puntero de destino a una tabla o vista en el Repositorio
  • ref le permite construir un gráfico de dependencias del modelo

Es Jinja quien agrega posibilidades casi ilimitadas a DBT. Los más utilizados:

  • Declaraciones if / else - declaraciones de ramificación
  • Para bucles - bucles
  • Variables - Variables
  • Macro: crear macros

Materialización: tabla, vista, incremental


Estrategia de materialización: un enfoque según el cual el conjunto resultante de datos del modelo se almacenará en el Repositorio.

En una consideración básica, esto es:

  • Tabla - tabla física en el almacenamiento
  • Ver - ver, tabla virtual en el repositorio

Hay estrategias de materialización más complejas:

  • Incremental: carga incremental (tablas de hechos grandes); se agregan nuevas líneas, se actualizan las modificadas, se borran las eliminadas 
  • Efímero: el modelo no se materializa directamente, pero participa como CTE en otros modelos
  • Cualquier otra estrategia que pueda agregar usted mismo

Además de las estrategias de materialización, se abren oportunidades para la optimización de almacenes específicos, por ejemplo:

  • Copo de nieve : tablas transitorias, comportamiento de fusión, agrupación de tablas, copia de concesiones, vistas seguras
  • Redshift : Distkey, Sortkey (intercalado, compuesto), Vistas de enlace tardío
  • BigQuery : particionamiento y agrupación de tablas, comportamiento de fusión, cifrado KMS, etiquetas y etiquetas
  • Spark : formato de archivo (parquet, csv, json, orc, delta), partición_by, clustered_by, cubos, estrategia_de_gramental

Los siguientes repositorios son compatibles actualmente:

  • Postgres
  • Redshift
  • Bigquery
  • Copo de nieve
  • Presto (parcialmente)
  • Chispa (parcialmente)
  • Microsoft SQL Server (adaptador de comunidad)

Mejoremos nuestro modelo:

  • Hacer su relleno incremental (incremental)
  • Agregar segmentación y ordenar claves para Redshift

--  : 
--  ,      (unique_key)
--   (dist),   (sort)
{{
  config(
       materialized='incremental',
       unique_key='order_id',
       dist="customer_id",
       sort="order_date"
   )
}}
 
{% set payment_methods = ['credit_card', 'coupon', 'bank_transfer', 'gift_card'] %}
 
with orders as (
 
   select * from {{ ref('stg_orders') }}
   where 1=1
   {% if is_incremental() -%}
       --        
       and order_date >= (select max(order_date) from {{ this }})
   {%- endif %} 
 
),
 
order_payments as (
 
   select * from {{ ref('order_payments') }}
 
),
 
final as (
 
   select
       orders.order_id,
       orders.customer_id,
       orders.order_date,
       orders.status,
       {% for payment_method in payment_methods -%}
       order_payments.{{payment_method}}_amount,
       {% endfor -%}
       order_payments.total_amount as amount
   from orders
       left join order_payments using (order_id)
 
)
 
select * from final

Gráfico de dependencia del modelo


Es un árbol de dependencias. Él es un DAG (gráfico acíclico dirigido).

DBT crea un gráfico basado en la configuración de todos los modelos de proyecto, o más bien los enlaces ref () dentro de los modelos a otros modelos. Tener un gráfico le permite hacer lo siguiente:

  • Ejecutar modelos en la secuencia correcta
  • Paralelización de escaparatismo
  • Ejecutar un subgrafo arbitrario 

Ejemplo de visualización gráfica:


Cada nodo del gráfico es un modelo, los bordes del gráfico están dados por la expresión ref.

Calidad de datos y documentación


Además de la formación de los propios modelos, DBT le permite probar una serie de afirmaciones sobre el conjunto de datos resultante, como:

  • No nulo
  • Único
  • Integridad de referencia: integridad de referencia (por ejemplo, customer_id en la tabla de pedidos corresponde a id en la tabla de clientes)
  • Lista válida coincidente

Puede agregar sus propias pruebas (pruebas de datos personalizados), como, por ejemplo, el% de desviación de los ingresos con indicadores hace un día, una semana o un mes. Cualquier suposición formulada como una consulta SQL puede ser una prueba.

De esta manera, las desviaciones no deseadas y los errores en los datos pueden detectarse en el escaparate del Almacenamiento.

En términos de documentación, DBT proporciona mecanismos para agregar, versionar y distribuir metadatos y comentarios a nivel de modelo e incluso atributos. 

Así es como se ve la adición de pruebas y documentación en el nivel del archivo de configuración:

 - name: fct_orders
   description: This table has basic information about orders, as well as some derived facts based on payments
   columns:
     - name: order_id
       tests:
         - unique #    
         - not_null #    null
       description: This is a unique identifier for an order
     - name: customer_id
       description: Foreign key to the customers table
       tests:
         - not_null
         - relationships: #   
             to: ref('dim_customers')
             field: customer_id
     - name: order_date
       description: Date (UTC) that the order was placed
     - name: status
       description: '{{ doc("orders_status") }}'
       tests:
         - accepted_values: #    
             values: ['placed', 'shipped', 'completed', 'return_pending', 'returned']

Y así es como se ve esta documentación en el sitio web generado:


Macros y Módulos


El propósito de DBT no es tanto convertirse en un conjunto de scripts SQL, sino proporcionar a los usuarios herramientas poderosas y ricas en características para construir sus propias transformaciones y distribuir estos módulos.

Las macros son conjuntos de construcciones y expresiones que se pueden llamar como funciones dentro de los modelos. Las macros le permiten reutilizar SQL entre modelos y proyectos de acuerdo con el principio de ingeniería DRY (Don't Repeat Yourself).

Ejemplo macro:

{% macro rename_category(column_name) %}
case
 when {{ column_name }} ilike  '%osx%' then 'osx'
 when {{ column_name }} ilike  '%android%' then 'android'
 when {{ column_name }} ilike  '%ios%' then 'ios'
 else 'other'
end as renamed_product
{% endmacro %}

Y su uso:

{% set column_name = 'product' %}
select
 product,
 {{ rename_category(column_name) }} --  
from my_table

DBT viene con un administrador de paquetes que permite a los usuarios publicar y reutilizar módulos y macros individuales.

Esto significa la capacidad de descargar y usar bibliotecas como:

  • dbt_utils : trabajando con fecha / hora, claves sustitutas, pruebas de esquema, pivote / unpivot y otros
  • Plantillas de escaparate listas para usar para servicios como Snowplow y Stripe 
  • Bibliotecas para almacenes de datos específicos, como Redshift 
  • Registro - Módulo de registro DBT

Una lista completa de paquetes está disponible en dbt hub .

Aún más funciones


Aquí describiré varias otras características e implementaciones interesantes que yo y el equipo utilizamos para construir un Data Warehouse en Wheely .

Separación de entornos de tiempo de ejecución DEV - TEST - PROD


Incluso dentro del mismo clúster DWH (dentro de diferentes esquemas). Por ejemplo, usando la siguiente expresión:

with source as (
 
   select * from {{ source('salesforce', 'users') }}
   where 1=1
   {%- if target.name in ['dev', 'test', 'ci'] -%}           
       where timestamp >= dateadd(day, -3, current_date)   
   {%- endif -%}
 
)

Este código literalmente dice: para entornos de desarrollo , prueba, ci, tome datos solo durante los últimos 3 días y no más. Es decir, la ejecución en estos entornos será mucho más rápida y requerirá menos recursos. Cuando se inicia en un entorno de producción , se ignorará la condición del filtro.

Materialización de codificación de columna alternativa


Redshift es un DBMS de columna que le permite especificar algoritmos de compresión de datos para cada columna individual. La elección de algoritmos óptimos puede reducir el espacio en disco ocupado en un 20-50%.

La macro redshift.compress_table ejecutará el comando ANALYZE COMPRESSION, creará una nueva tabla con los algoritmos de codificación de columna recomendados indicados por las teclas de segmentación (dist_key) y sort_key (sort_key), transferirá los datos y eliminará la copia anterior si es necesario.

Macro Firma:

{{ compress_table(schema, table,
                 drop_backup=False,
                 comprows=none|Integer,
                 sort_style=none|compound|interleaved,
                 sort_keys=none|List<String>,
                 dist_style=none|all|even,
                 dist_key=none|String) }}

Lanzamientos de modelos de registro


Para cada ejecución del modelo, puede colgar ganchos que se ejecutarán antes del lanzamiento o inmediatamente después de la creación del modelo:

   pre-hook: "{{ logging.log_model_start_event() }}"
   post-hook: "{{ logging.log_model_end_event() }}"

El módulo de registro le permitirá registrar todos los metadatos necesarios en una tabla separada, según la cual posteriormente puede auditar y analizar áreas problemáticas (cuellos de botella).

Así es como se ve el tablero en los datos de búsqueda en Looker:


Automatización de almacenamiento


Si utiliza alguna extensión de la funcionalidad del Almacenamiento utilizada, como UDF (Funciones definidas por el usuario), entonces es muy conveniente implementar versiones en estas funciones, control de acceso y la implementación automática de nuevas versiones en DBT.

Usamos UDF en Python para calcular valores hash, dominios de dominio de correo y decodificar máscara de bits.

Ejemplo de macro que crea UDF en cualquier tiempo de ejecución (dev, test, prod):

{% macro create_udf() -%}
 
 {% set sql %}
       CREATE OR REPLACE FUNCTION {{ target.schema }}.f_sha256(mes "varchar")
           RETURNS varchar
           LANGUAGE plpythonu
           STABLE
       AS $$  
           import hashlib
           return hashlib.sha256(mes).hexdigest()
       $$
       ;
 {% endset %}
  
 {% set table = run_query(sql) %}
 
{%- endmacro %}

En Wheely, utilizamos Amazon Redshift, que se basa en PostgreSQL. Para Redshift, es importante recopilar regularmente estadísticas sobre tablas y liberar espacio en disco: los comandos ANALYZE y VACUUM, respectivamente.

Para hacer esto, los comandos de la macro redshift_maintenance se ejecutan todas las noches:

{% macro redshift_maintenance() %}
 
   {% set vacuumable_tables=run_query(vacuumable_tables_sql) %}
 
   {% for row in vacuumable_tables %}
       {% set message_prefix=loop.index ~ " of " ~ loop.length %}
 
       {%- set relation_to_vacuum = adapter.get_relation(
                                               database=row['table_database'],
                                               schema=row['table_schema'],
                                               identifier=row['table_name']
                                   ) -%}
       {% do run_query("commit") %}
 
       {% if relation_to_vacuum %}
           {% set start=modules.datetime.datetime.now() %}
           {{ dbt_utils.log_info(message_prefix ~ " Vacuuming " ~ relation_to_vacuum) }}
           {% do run_query("VACUUM " ~ relation_to_vacuum ~ " BOOST") %}
           {{ dbt_utils.log_info(message_prefix ~ " Analyzing " ~ relation_to_vacuum) }}
           {% do run_query("ANALYZE " ~ relation_to_vacuum) %}
           {% set end=modules.datetime.datetime.now() %}
           {% set total_seconds = (end - start).total_seconds() | round(2)  %}
           {{ dbt_utils.log_info(message_prefix ~ " Finished " ~ relation_to_vacuum ~ " in " ~ total_seconds ~ "s") }}
       {% else %}
           {{ dbt_utils.log_info(message_prefix ~ ' Skipping relation "' ~ row.values() | join ('"."') ~ '" as it does not exist') }}
       {% endif %}
 
   {% endfor %}
 
{% endmacro %}

DBT Cloud


Es posible utilizar DBT como servicio (Servicio gestionado). En un conjunto:

  • IDE web para desarrollar proyectos y modelos
  • Configuración de trabajo y configuración de programación
  • Acceso simple y conveniente a los registros
  • Sitio web con la documentación de su proyecto.
  • Conexión CI (integración continua)


Conclusión


Cocinar y consumir DWH es tan agradable y beneficioso como beber batidos. DBT consta de Jinja, extensiones personalizadas (módulos), compilador, motor (ejecutor) y administrador de paquetes. Una vez que haya reunido estos elementos, obtendrá un entorno de trabajo completo para su Data Warehouse. Apenas hay una mejor manera de gestionar las transformaciones en DWH hoy.



Las creencias seguidas por los desarrolladores de DBT se formulan de la siguiente manera:

  • El código, no la GUI, es la mejor abstracción para expresar una lógica analítica compleja
  • El trabajo con datos debe adaptar las mejores prácticas de desarrollo de software (Ingeniería de software)

  • La infraestructura de datos críticos debe ser controlada por la comunidad de usuarios como software de código abierto
  • No solo las herramientas de análisis, sino que el código se convertirá cada vez más en parte de la comunidad Open Source.

Estas creencias fundamentales han generado un producto que actualmente utilizan más de 850 empresas, y forman la base de muchas extensiones interesantes que se crearán en el futuro.

Para aquellos que estén interesados, hay un video de una lección abierta que pasé hace unos meses como parte de una lección abierta en OTUS - Data Build Tool para el repositorio de Amazon Redshift .

Además de DBT y Data Warehouses, como parte del curso de Ingeniero de datos en la plataforma OTUS, mis colegas y yo impartimos clases sobre una serie de otros temas relevantes y modernos:

  • Conceptos arquitectónicos para aplicaciones de Big Data
  • Practica con Spark y Spark Streaming
  • Métodos y herramientas de aprendizaje para cargar fuentes de datos.
  • Construcción de vitrinas analíticas en DWH
  • NoSQL: HBase, Cassandra, ElasticSearch
  •  
  • :

:


  1. DBT documentation — Introduction
  2. What, exactly, is dbt? — DBT 
  3. Data Build Tool Amazon Redshift — YouTube, OTUS
  4. Greenplum — 15 2020
  5. Data Engineering — OTUS
  6. Building a Mature Analytics Workflow
  7. It’s time for open source analytics — Open Source
  8. Continuous Integration and Automated Build Testing with dbtCloud — CI DBT
  9. Getting started with DBT tutorial — ,
  10. Jaffle shop — Github DBT Tutorial — Github,




.



All Articles