Polars streaming: processe dados maiores que a RAM — sem Spark
Como a streaming engine do Polars processa datasets que não cabem na memória usando scan_parquet + sink_parquet, mantendo o uso de RAM constante e dispensando um cluster.
Existe um reflexo comum em times de dados: assim que um arquivo passa de alguns gigabytes e o pandas começa a estourar a memória com um MemoryError, a resposta padrão é "sobe pro Spark". Só que boa parte desses casos não é um problema de big data de verdade — é um problema de dado que não cabe na RAM de uma vez. E esse é exatamente o cenário que o Polars resolve num único processo Python, sem cluster, sem JVM, sem infraestrutura extra.
A diferença está em como o dado é processado. O pandas é eager: ele carrega tudo na memória e executa operação por operação. O Polars tem uma streaming engine que trabalha com o dado em lotes pequenos, mantendo um footprint de memória constante — não importa se o arquivo tem 1 GB ou 500 GB.
Os três conceitos que fazem isso funcionar
1. Lazy API: scan_parquet não lê nada
Quando você chama pl.read_parquet(), o Polars carrega o arquivo inteiro na memória — comportamento eager, igual ao pandas. Já pl.scan_parquet() faz algo diferente: ele não lê os dados. Ele cria um LazyFrame, que é apenas um plano lógico descrevendo as operações que você pretende executar.
Nada acontece até você chamar explicitamente .collect() (materializa na memória) ou .sink_parquet() (grava em disco). Isso é o que abre a porta para o processamento larger-than-RAM.
import polars as pl
# nada é lido aqui — apenas um plano lazy é criado
lf = pl.scan_parquet("vendas/*.parquet")
2. Query planner: otimização antes da execução
Como o Polars conhece o plano inteiro antes de executar, ele otimiza a consulta — de um jeito que uma abordagem eager não consegue:
- Projection pushdown: lê do disco só as colunas que você realmente usa.
- Predicate pushdown: empurra os filtros para o momento da leitura, descartando linhas antes de carregá-las.
- Reordenação e fusão de operações para minimizar passagens sobre os dados.
Você escreve o código de forma declarativa e encadeada; o planejador cuida do resto.
res = (
lf.filter(pl.col("uf") == "SP")
.group_by("loja")
.agg(pl.col("valor").sum())
)
3. Streaming sinks: escreva resultados maiores que a RAM
O passo final é onde a mágica acontece. Em vez de materializar tudo com .collect(), você usa um sink:
res.sink_parquet("resumo.parquet")
O sink_parquet() avalia a consulta em modo streaming e escreve o resultado direto no disco, lote a lote. Isso garante um uso de memória constante independentemente do tamanho do dataset — e permite que o resultado final seja maior que a RAM disponível.
Por baixo dos panos, operadores como group_by, sort e os lados build e probe de um equi-join são spillable: quando detectam pressão de memória, escrevem o estado acumulado em arquivos temporários, liberando RAM. Os file sinks (sink_parquet, sink_csv, sink_ipc) são spillable por natureza — eles já escrevem em disco por definição.
O pipeline completo
Juntando tudo, um pipeline que processa 100 GB de dados numa máquina com 8 GB de RAM fica assim:
import polars as pl
# 1. plano lazy — nada é carregado
lf = pl.scan_parquet("vendas/*.parquet")
# 2. transformações otimizadas pelo query planner
res = (
lf.filter(pl.col("uf") == "SP")
.group_by("loja")
.agg(pl.col("valor").sum())
)
# 3. execução em streaming, gravando em disco lote a lote
res.sink_parquet("saida.parquet")
Sem cluster. Sem MemoryError. Sem reescrever nada em PySpark.
Por que isso importa (além da velocidade)
O Polars é construído em Rust sobre o Apache Arrow e costuma entregar ganhos de 5–50x sobre o pandas em cargas reais. Mas o argumento aqui não é só desempenho — é arquitetura:
- Menos infraestrutura: um script Python substitui um cluster que precisa ser provisionado, monitorado e pago.
- Menos custo: sem nós ociosos, sem overhead de orquestração distribuída para um volume que roda tranquilo em uma máquina.
- Mais portabilidade: o mesmo código roda no seu notebook, num container pequeno ou numa VM — sem dependências de ecossistema.
E o ecossistema encaixa bem: o DuckDB consegue consultar um DataFrame Polars diretamente na memória via a interface Arrow, com zero overhead de serialização. Muitos times usam os dois no mesmo pipeline — Polars para manipulação de DataFrame e feature engineering, DuckDB para análise em SQL sobre os mesmos dados.
Quando ainda vale a pena usar Spark
Ser justo com a ferramenta certa importa. O Spark continua fazendo sentido quando:
- você precisa de escala horizontal real, com dados distribuídos entre vários nós;
- o volume ultrapassa o que uma única máquina (mesmo com streaming e spill em disco) consegue processar em tempo hábil;
- você já tem o ecossistema montado (Databricks, governança no Unity Catalog, jobs em produção) e a consistência operacional pesa mais que a simplicidade.
Para o processamento single-node larger-than-RAM que aparece na maioria dos times — agregações, joins, limpeza e transformação de arquivos grandes —, a streaming engine do Polars cobre o caso com folga. E o roadmap reforça a direção: o Polars 2.0 traz uma streaming engine redesenhada (paralelismo morsel-driven + máquinas de estado assíncronas em Rust), e a Polars Cloud estende o motor open-source para execução serverless, escala horizontal em dados particionados e tolerância a falhas.
Resumo em três passos
scan_parquetcria um plano lazy — nada é lido até você pedir.- Encadeie
filter/group_by/agg; o query planner otimiza a consulta inteira. sink_parquetexecuta em streaming e grava em disco lote a lote — resultados maiores que a RAM, com memória constante e sem cluster.
Antes de provisionar um cluster no próximo pipeline, meça se um scan_parquet + sink_parquet não resolve. Na maioria das vezes, resolve.
Artigos relacionados
Metric Views no Unity Catalog: defina o KPI uma vez, use em todo lugar
Como as Metric Views do Databricks Unity Catalog transformam KPIs de negócio em objetos governados e reutilizáveis — com o passo a passo em YAML, a função MEASURE(), o padrão de consulta, e quando (ou não) usar em 2026.
Ler artigoCDC no SSIS: carga incremental sem varrer a tabela inteira
Como usar Change Data Capture com o CDC Control Task para transformar cargas full em cargas incrementais no SSIS — com código, o padrão de estado por LSN e quando (ou não) usar em 2026.
Ler artigoGostou? Veja os e-books para conteúdo aprofundado.
E-books