Cómo crear particiones dinámicas en tablas Delta en el Lakehouse
Este tutorial muestra cómo crear particiones dinámicas en una tabla Delta en el Lakehouse para mejorar la lectura y escritura de datos. Particionar correctamente reduce costes de I/O, acelera consultas y evita archivos pequeños cuando se escribe en paralelo. Además, particiones bien definidas facilitan operaciones de limpieza y retención y ayudan al motor a aplicar partition pruning, lo que puede reducir el volumen leído entre 10x y 100x en escenarios comunes.
Requisitos previos
- Cuenta con acceso a Microsoft Fabric y un Lakehouse creado.
- Permisos para crear/alterar tablas Delta en el Lakehouse.
- Archivos de ejemplo (CSV/JSON) cargados en un path del Lakehouse o OneLake.
- Conocimientos básicos de SQL y PySpark.
Paso 1: Elegir la columna de partición correcta
Por qué: una buena columna de partición equilibra el número de archivos y la selectividad de las consultas. Evite columnas con alta cardinalidad (p. ej.: ID único con millones de valores) — eso crea muchas particiones pequeñas y aumenta la sobrecarga de metadatos. Tampoco use columnas con cardinalidad muy baja (p. ej.: un campo con siempre el mismo valor), porque ahí no hay beneficio. Para datos temporales, usar una jerarquía como year/month[/day] es una práctica común. Por ejemplo:
- Fechas: year/month — buen equilibrio para años con ~12 meses y cientos de miles a millones de registros por mes.
- Alta cardinalidad: user_id con 10M de usuarios — no usar como partición.
- Baja cardinalidad: country con 5-10 valores — no suficiente para reducción significativa de I/O.
Regla práctica: cada partición debe contener al menos decenas de MB a cientos de MB (p. ej.: 100-500 MB) para ser eficiente con archivos Delta. Si sus particiones quedan consistentemente por debajo de ~64 MB, considere reducir la granularidad de las particiones.
Paso 2: Crear una tabla Delta no particionada (opcional, para importar datos iniciales)
Es útil empezar sin partición para validar esquema y luego convertir a particionada cuando tenga carga de datos real. Esto ayuda a verificar tipos y nulos con datos reales antes de definir particiones definitivas. Ejemplo en SQL en el SQL Analytics endpoint o notebook:
CREATE TABLE IF NOT EXISTS my_db.raw_events_delta
USING DELTA
LOCATION 'lakehouse:////raw/events'
AS SELECT * FROM csv.`/paths/events/*.csv`;
Después de importar algunos millones de filas, inspeccione muestras y observe la distribución temporal para decidir si usa year/month/day.
Paso 3: Crear tabla Delta particionada con esquema final
Crée la tabla con PARTITIONED BY. Aquí usamos year y month extraídos de la columna event_timestamp. Esto crea una estructura de carpetas organizada, por ejemplo /year=2024/month=06/, que facilita el pruning y la gestión de datos.
CREATE TABLE IF NOT EXISTS my_db.events_delta
(
event_id STRING,
event_timestamp TIMESTAMP,
user_id STRING,
event_type STRING,
payload STRING
)
USING DELTA
PARTITIONED BY (year, month)
LOCATION 'lakehouse:////tables/events';
Nota: elegir PARTITIONED BY evita la necesidad de crear columnas físicas separadas para year y month si prefiere extraerlas en el SELECT, pero tener columnas explícitas facilita las consultas y los índices.
Paso 4: Escribir datos usando particiones dinámicas con PySpark
Al escribir, calcule year y month y use write.partitionBy para crear particiones dinámicas. Esto garantiza que los archivos se escriben en la carpeta correcta según la fecha del evento. Ejemplo PySpark mínimo:
from pyspark.sql.functions import year, month, to_timestamp
df = spark.read.csv('/mnt/lake/events/*.csv', header=True)
df = df.withColumn('event_timestamp', to_timestamp(df.event_timestamp))
df = df.withColumn('year', year(df.event_timestamp))
df = df.withColumn('month', month(df.event_timestamp))
(df.write
.format('delta')
.mode('append')
.partitionBy('year','month')
.save('lakehouse:////tables/events'))
Ejemplo concreto: si tiene 10M de registros por mes y un cluster con 200 ejecutores, estime los archivos finales por partición y ajuste repartition para alcanzar ~128 MB por archivo.
Paso 5: Evitar archivos pequeños (small files) al escribir en paralelo
Error común: cada ejecutor escribe pocos MB, creando cientos de archivos pequeños por partición, lo que degrada el rendimiento. Soluciones:
- Aumentar el tamaño de los archivos con coalesce/repartition antes de escribir: por ejemplo, si pretende archivos ~128 MB y tiene 1 TB de datos, estime 8000 archivos y haga repartition(8000).
- Reparticionar por una clave de partición lógica (p. ej.: concat year-month) para agrupar datos de la misma partición antes del write.
- Ejecutar OPTIMIZE / compaction después de la ingestión para combinar archivos pequeños (cuando esté soportado en el entorno).
# Reparticionar por partición aproximada antes de escribir
from pyspark.sql.functions import concat_ws
# Crear una columna de partition_key para reparticionar
df = df.withColumn('partition_key', concat_ws('-', df.year.cast('string'), df.month.cast('string')))
# Repartition por partition_key para reducir archivos
df = df.repartition('partition_key')
(df.drop('partition_key')
.write
.format('delta')
.mode('append')
.partitionBy('year','month')
.save('lakehouse:////tables/events'))
Paso 6: Actualizar esquema o añadir particiones
Si añade nuevas columnas, use ALTER TABLE para evitar reescribir toda la tabla. Para detectar nuevas carpetas/particiones externas, algunos entornos soportan comandos como MSCK REPAIR; otros ofrecen SHOW PARTITIONS. Ejemplos:
ALTER TABLE my_db.events_delta ADD COLUMNS (device STRING);
-- Listar particiones
SHOW PARTITIONS my_db.events_delta;
Si es necesario migrar de no particionado a particionado con millones de filas, haga una escritura controlada en batch para evitar picos de I/O.
Verificar el resultado
Confirme que las particiones existen y que las consultas realizan partition pruning. Ejecute:
-- Ver particiones físicas
SHOW PARTITIONS my_db.events_delta;
-- Prueba de partition pruning (verifica planes)
EXPLAIN SELECT * FROM my_db.events_delta WHERE year = 2024 AND month = 6;
-- Contar por partición
SELECT year, month, COUNT(*) FROM my_db.events_delta GROUP BY year, month ORDER BY year, month;
Si EXPLAIN muestra que solo se leen las rutas year=2024/month=6, el pruning está funcionando. Monitorice tiempos de consulta y I/O: idealmente verá una reducción significativa cuando el filtro coincida con particiones.
Conclusión
Las particiones dinámicas en tablas Delta en el Lakehouse mejoran el rendimiento y el coste cuando están bien elegidas. Empiece con una granularidad conservadora (year/month), monitorice la distribución de datos y el tamaño medio de los archivos y ajuste repartition al escribir. Próximos pasos: automatizar la partición en pipelines de ingestión, usar OPTIMIZE y ZORDER (cuando esté soportado) para mejorar el ordenamiento interno y reducir la latencia de lectura, y crear alertas para detectar crecimiento de archivos pequeños. Consejo práctico: apunte a archivos de ~128–256 MB por partición y revise la estrategia cada trimestre según el crecimiento de los datos.