Otimizador de Processamento em Lote
Otimize cargas de trabalho de processamento de dados em lote usando Spark, Polars, DuckDB e pandas com foco em eficiência de memória, paralelismo e redução de custos.
Gatilhos de Ativação
Ative em: "processamento em lote", "otimização Spark", "Polars", "DuckDB", "performance pandas", "data frame", "otimização de shuffle", "skew de partição", "otimização de memória", "falta de memória"
NÃO use para: Streaming em tempo real → streaming-pipeline-architect | Ajuste de SQL de warehouse → data-warehouse-optimizer | Orquestração de pipeline → airflow-dag-orchestrator
Início Rápido
- Escolha a ferramenta certa — DuckDB para análise em um único nó, Polars para DataFrames, Spark para distribuído
- Analise primeiro — identifique gargalos (shuffle, skew, memória) antes de otimizar
- Reduza os dados cedo — filtre e selecione colunas o mais cedo possível no pipeline
- Evite shuffles — transmita tabelas pequenas (broadcast), pré-partitione dados, use joins map-side
- Dimensionamento adequado — ajuste memória/cores do executor ao tamanho real dos dados
Capacidades Principais
| Domínio | Tecnologias |
|---|---|
| Distribuído | Apache Spark 3.5+, Dask, Ray |
| Nó Único | DuckDB 1.1+, Polars 1.x, pandas 2.2+ |
| Formatos de Arquivo | Parquet, Arrow IPC, Delta Lake, Iceberg |
| Otimização | AQE (Spark), avaliação lazy (Polars), varreduras colunares |
| Cloud | Databricks, EMR, Dataproc, serverless Spark |
Padrões de Arquitetura
Árvore de Decisão para Seleção de Ferramentas
Tamanho dos Dados?
├─ < 10 GB → DuckDB (SQL) ou Polars (DataFrame)
│ Máquina única, zero configuração, iteração mais rápida
│
├─ 10-100 GB → Polars (lazy) ou DuckDB (out-of-core)
│ Ainda em máquina única com spill para disco
│
└─ > 100 GB → Spark (distribuído)
Cluster multi-nó, joins baseados em shuffle
Complexidade?
├─ Foco em SQL → DuckDB (motor SQL mais rápido para análise)
├─ DataFrame → Polars (10x mais rápido que pandas, avaliação lazy)
└─ ML Complexo → Spark + MLlib ou Spark + Ray
Padrões de Otimização Spark
from pyspark.sql import SparkSession
import pyspark.sql.functions as F
spark = SparkSession.builder \
.config("spark.sql.adaptive.enabled", "true") \
.config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
.config("spark.sql.adaptive.skewJoin.enabled", "true") \
.getOrCreate()
# BOM: transmitir tabela de dimensão pequena (< 100MB)
from pyspark.sql.functions import broadcast
resultado = large_df.join(broadcast(small_dim_df), "chave")
# BOM: pushdown de predicado — filtrar antes do join
pedidos = spark.read.parquet("s3://data/orders/") \
.filter(F.col("order_date") >= "2026-01-01") \
.select("order_id", "customer_id", "amount") # poda de colunas
# RUIM: collect() em grande conjunto de dados — causa OOM no driver
# todos_dados = large_df.collect() # NUNCA faça isso
# BOM: escrever saída particionada
resultado.repartition(200) \
.write.mode("overwrite") \
.partitionBy("order_date") \
.parquet("s3://output/results/")
Avaliação Lazy do Polars
import polars as pl
# Modo lazy: constrói plano de consulta, otimiza, depois executa
resultado = (
pl.scan_parquet("data/orders/*.parquet") # varredura lazy
.filter(pl.col("order_date") >= "2026-01-01")
.join(
pl.scan_parquet("data/customers/*.parquet"),
on="customer_id",
how="inner"
)
.group_by("region")
.agg([
pl.col("amount").sum().alias("total_revenue"),
pl.col("order_id").n_unique().alias("order_count"),
])
.sort("total_revenue", descending=True)
.collect() # executa o plano otimizado
)
# Polars otimiza: pushdown de predicado, pushdown de projeção,
# reordenação de joins — tudo automaticamente via avaliação lazy
Anti-Padrões
- pandas para >5GB — pandas carrega tudo na memória; use Polars (lazy) ou DuckDB para dados médios, Spark para grandes
- Coletar para o driver —
df.collect()oudf.toPandas()em grandes DataFrames Spark causa OOM; agregue primeiro - Ignorar skew de partição — uma partição com 10x mais dados que outras gargalo toda a tarefa; use AQE ou salting
- Ler todas as colunas — sempre selecione apenas as colunas necessárias; o formato colunar Parquet ignora colunas não utilizadas
- Arquivos de saída minúsculos — muitos arquivos de saída pequenos (< 128MB) tornam leituras downstream lentas; coalesce antes de escrever
Checklist de Qualidade
- Ferramenta compatível com o tamanho dos dados (DuckDB/Polars < 100GB, Spark > 100GB)
- Colunas podadas cedo (selecionar apenas o necessário)
- Filtros aplicados no nível da varredura (pushdown de predicado)
- Tabelas pequenas transmitidas em joins (broadcast) (< 100MB)
- AQE do Spark ativado (adaptive query execution)
- Nenhum
collect()em grandes conjuntos de dados (agregue antes de coletar) - Arquivos de saída dimensionados entre 128MB e 1GB (coalesce/repartition antes de escrever)
- Skew de partição monitorado e mitigado (salting ou AQE)
- Tarefa analisada: estágios da UI do Spark,
.explain()do Polars,EXPLAIN ANALYZEdo DuckDB - Memória dimensionada adequadamente: memória do executor >= 2x a maior partição