Saltar a contenido

M贸dulo 2: Conectividad y Transformaci贸n

La capacidad de analizar datos a gran escala depende cr铆ticamente de c贸mo movemos la informaci贸n desde su origen hacia nuestro motor de c贸mputo. En este m贸dulo, exploramos el patr贸n de Ingestion Pipeline y el impacto de la latencia de E/S en el rendimiento.

El cuello de botella: IO Latency

En la arquitectura de datos moderna, uno de los mayores desaf铆os es la IO Latency (latencia de Entrada/Salida). Cuando extraemos datos de un RDBMS tradicional (como MariaDB o MySQL), nos enfrentamos a una discrepancia fundamental de formatos:

  1. RDBMS (Row-based): Las bases de datos relacionales almacenan los datos en filas. Esto es ideal para transacciones (OLTP), donde necesitamos leer o escribir un registro completo.
  2. An谩lisis (Columnar): Para el an谩lisis de datos (OLAP), necesitamos leer columnas enteras para calcular agregaciones (sumas, promedios). Leer filas completas cuando solo necesitamos una columna genera una lectura innecesaria de datos, saturando el ancho de banda de E/S.

El reto consiste en mover datos desde un formato basado en filas hacia un formato columnar de manera eficiente, minimizando el tiempo de transferencia y la CPU dedicada a la serializaci贸n.

Enfoque en Apache Arrow

Para resolver este problema, Polars utiliza Apache Arrow como su n煤cleo de memoria. Arrow no es solo un formato de almacenamiento, sino un est谩ndar de memoria columnar.

驴C贸mo gestiona Polars la ingesta?

Cuando Polars lee datos de una base de datos a trav茅s de conectores optimizados (como connectorx), el proceso ocurre de la siguiente manera:

  • Stream de Filas \(\rightarrow\) Arrays Columnares: El conector extrae el flujo de filas del RDBMS y, en lugar de convertirlas en objetos Python lentos, las organiza inmediatamente en arrays contiguos de memoria siguiendo la especificaci贸n de Arrow.
  • Zero-copy: Al utilizar Arrow, Polars puede pasar estos datos entre diferentes herramientas o etapas del pipeline sin necesidad de copiar los datos en memoria, eliminando el coste de serializaci贸n/deserializaci贸n.
  • Eficiencia de CPU: Al estar los datos alineados en columnas, el procesador puede aplicar instrucciones SIMD (Single Instruction, Multiple Data) para procesar miles de valores en un solo ciclo de reloj.

Implementaci贸n del Ingestion Pipeline (RDBMS 馃憠 Polars)

A continuaci贸n, implementamos el patr贸n b谩sico de ingesta: extraer datos de MariaDB y cargarlos en un DataFrame de Polars para su an谩lisis.

1. Preparaci贸n del entorno

Necesitamos instalar polars y connectorx (un conector escrito en Rust extremadamente r谩pido para cargar datos de SQL a Arrow).

pip install polars connectorx

2. Lectura optimizada v铆a ConnectorX

connectorx es capaz de leer la base de datos y producir directamente una tabla Arrow, evitando el cuello de botella de Python.

import polars as pl
import connectorx as cx
import pyarrow.compute as pc

# Configuraci贸n de conexi贸n
conn = 'mysql://usuario:contrase帽a@mariadb:3306/kafka'
query = 'SELECT * FROM songs'

# Leer produciendo una tabla Arrow directamente en memoria
ta = cx.read_sql(conn, query, return_type='arrow')
print("Esquema de la Tabla Arrow:", ta.schema)

# Ejemplo de filtrado r谩pido sobre la tabla Arrow antes de pasar a Polars
pjta = ta.filter(pc.equal(ta['artist'], 'Pearl Jam'))
print(pjta.select(["album","title"]))

3. Ingesta directa a Polars DataFrame

Para la mayor铆a de los casos, la funci贸n read_database_uri es la forma m谩s sencilla y eficiente de implementar este pipeline, ya que abstrae la gesti贸n de Arrow internamente.

import polars as pl

uri = "mysql://usuario:contrase帽a@mariadb:3306/kafka"
query = "SELECT * FROM songs"

# Ingesta directa: RDBMS -> Polars DataFrame
df = pl.read_database_uri(query=query, uri=uri)
print(df.head())

4. Conectividad mediante SQLAlchemy

En entornos donde se requiere una gesti贸n m谩s robusta de las conexiones (pooling, transacciones), podemos utilizar SQLAlchemy.

from sqlalchemy import create_engine
import polars as pl

# Configuraci贸n de conexi贸n
username = 'usuario'
password = 'contrase帽a'
host = 'mariadb:3306'
database = 'kafka'

# Creaci贸n del motor (Engine) con pool de conexiones
dbcon = create_engine(
    f'mariadb+mariadbconnector://{username}:{password}@{host}/{database}',
    pool_size=10, 
    max_overflow=20
)

# Consultar la base de datos usando la conexi贸n de SQLAlchemy
df = pl.read_database(query="SELECT * FROM songs", connection=dbcon)

Escritura de datos en la Base de Datos

El flujo inverso tambi茅n es posible. Podemos tomar un dataset procesado en Polars y volcarlo en un RDBMS para persistir los resultados.

# 1. Leer datos de una fuente local (ej. CSV)
dfpl = pl.read_csv("Eurostar/stations.csv")

# 2. Volcar el DataFrame columnar en la tabla de la base de datos (Row-based)
dfpl.write_database(
    table_name='stations', 
    connection=dbcon, 
    engine='sqlalchemy', 
    if_table_exists="replace"
)

Referencias