Brains Up AnalyticsBRAINSUPAnalytics
PolarsPythonStreamingData EngineeringDuckDB

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.

Por Dione Fraga · Databricks Certified Professional30 de julho de 20265 min de leitura

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

  1. scan_parquet cria um plano lazy — nada é lido até você pedir.
  2. Encadeie filter / group_by / agg; o query planner otimiza a consulta inteira.
  3. sink_parquet executa 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

Gostou? Veja os e-books para conteúdo aprofundado.

E-books