BIX Tecnologia

Databricks: Guia técnico de otimização de Pipelines no Apache Spark

7 min de leitura
Sabrina Oliveira
Sabrina Oliveira
Diagrama técnico mostrando a relação entre Driver, Workers e Executors em um cluster Apache Spark.

Tire o seu projeto do papel

Compartilhar

A otimização no Apache Spark é o processo de ajustar a distribuição de dados e o uso de memória para reduzir o tempo de execução e os custos operacionais de pipelines de dados. Para alcançar a máxima eficiência, nós precisamos garantir que as tarefas sejam processadas em paralelo de forma equilibrada, evitando a movimentação excessiva de informações pela rede do cluster.

O Apache Spark funciona como uma plataforma de processamento distribuído baseada em in-memory computing. Diferente de sistemas tradicionais, a maioria das transformações ocorre diretamente na memória dos nós, o que acelera a análise de grandes volumes de dados. No entanto, o desempenho pode ser afetado por fatores como o uso ineficiente de memória, má distribuição de tarefas e recursos inadequados para a carga de trabalho.

Entenda o processamento distribuído e em memória

A eficiência do Spark vem da sua capacidade de manter os dados na memória (RAM) durante o processamento. Quando os limites de memória são atingidos, o sistema utiliza o disco, o que torna a operação mais lenta.

  • In-memory computing: As operações acontecem nos nós do cluster para evitar o I/O de disco.

  • Distribuição de tarefas: O trabalho é dividido em partes menores para ser executado simultaneamente.

  • Pontos de atenção: O desempenho é prejudicado quando há movimentação excessiva de dados (shuffle) ou quando os recursos de CPU não são suficientes para o volume processado.

Arquitetura do Spark no Databricks

Ao configurar um cluster no Databricks, nós definimos a estrutura que executará o código. Essa arquitetura é dividida em dois papéis principais:

  • Driver: É o coordenador central. Ele cria o plano de execução, gerencia as informações do estado do job e distribui as tarefas para os trabalhadores.

  • Workers: São os nós que realizam o processamento. Cada worker roda uma JVM (Java Virtual Machine).

  • Executors: Processos dentro dos workers que executam as tarefas e armazenam dados em cache.

  • Slots: Representam o número de núcleos de CPU disponíveis para execução paralela dentro de um executor.

O paralelismo total do cluster é determinado pela soma de todos os slots disponíveis nos workers. Se houver poucos slots para muitos dados, o processamento será sequencial e lento.

Hierarquia de execução: jobs, stages e tasks

Para otimizar um pipeline, você deve entender como o Spark organiza o trabalho internamente. Existe uma hierarquia clara de execução:

  • Job: É iniciado sempre que uma ação é disparada (como write, count ou display).

  • Stage: O job é dividido em estágios. A divisão entre um stage e outro ocorre sempre que há necessidade de redistribuir dados (shuffle).

  • Task: É a menor unidade de trabalho, executada em um único slot de CPU sobre uma partição de dados.

O shuffle é uma operação custosa, pois consome rede, CPU e memória para mover dados entre diferentes nós. Reduzir o número de shuffles é uma das melhores formas de economizar recursos.

RDDs e partições de dados

Os RDDs (Resilient Distributed Datasets) são a base do Spark. Eles possuem três características fundamentais:

  • Imutabilidade: Uma vez criados, não podem ser alterados, apenas transformados em novos RDDs.

  • Distribuição: Os dados são divididos em partições lógicas processadas em paralelo.

  • Tolerância a falhas: O Spark consegue recalcular partições perdidas caso um nó falhe.

É importante diferenciar as partições de execução (Spark partitions) das partições de armazenamento (Hive partitions). Enquanto as de execução definem o paralelismo em memória, as de armazenamento organizam como os arquivos são salvos fisicamente no data lake.

O conceito de lazy evaluation

No Spark, o código não é executado linha por linha assim que é escrito. Ele utiliza a_ lazy evaluation_ (avaliação preguiçosa), dividindo as operações em dois tipos:

  • Transformations: Definem o que fazer com os dados (como filter, select e join). Elas apenas criam um plano de execução.

  • Actions: Comandos que forçam a execução do plano (como write, show ou count).

Inserir ações desnecessárias como o display() entre transformações pode causar reprocessamentos e aumentar o tempo de execução.

Diferença entre transformações narrow e wide

  • **Narrow transformations: **Uma partição de entrada gera apenas uma de saída (ex: filter, select). Não exigem shuffle e são muito eficientes.

  • Wide transformations: Uma partição de entrada pode contribuir para várias de saída (ex: join, groupBy, distinct). Exigem shuffle, o que aumenta o custo e o tempo.

Monitoramento com a Spark UI

A Spark UI é a principal ferramenta de observabilidade no Databricks. Ela permite identificar onde estão as limitações de performance do seu código.

  • Aba Jobs: Mostra o progresso geral e o tempo total de execução.

  • Aba Stages: Detalha cada estágio, permitindo ver a duração das tarefas e métricas de shuffle.

  • Aba Executors: Exibe o uso de memória e CPU por nó, ajudando a identificar se o cluster está bem dimensionado.

  • Métricas de Task: Permite observar se as tarefas estão desbalanceadas (algumas muito lentas e outras rápidas).

Otimização de leitura com Data Skipping e Z-Order

Um dos maiores custos em Spark é a leitura de arquivos. O objetivo deve ser ler apenas o necessário utilizando o Data Skipping.

  • Data Skipping: O Spark e o Delta Lake utilizam estatísticas (mínimo e máximo) para ignorar arquivos que não atendem aos filtros da consulta.

  • OPTIMIZE: Compacta arquivos pequenos em arquivos maiores para melhorar a eficiência da leitura.

  • Z-Order: Reorganiza os dados fisicamente para agrupar informações relacionadas, o que potencializa drasticamente o Data Skipping.

Em projetos da BIX Tecnologia, a aplicação de Z-ORDER reduziu a leitura de centenas de arquivos para apenas um, baixando o tempo de consulta de 14 para 4 segundos. Para manter a performance, use o comando VACUUM periodicamente para remover arquivos antigos e reduzir custos de armazenamento.

Gerenciamento de spill e data skew

Problemas de memória são comuns em processamentos de grande escala. Os dois principais são o spill e o skew:

  • Spill: Ocorre quando os dados não cabem na memória e o Spark precisa gravá-los temporariamente no disco. Isso aumenta muito o tempo de processamento.

  • Data Skew (Enviesamento): Acontece quando os dados estão mal distribuídos e uma única tarefa fica muito maior que as outras, travando a conclusão do stage.

Para mitigar esses problemas, nós recomendamos ajustar o número de spark.sql.shuffle.partitions, usar o AQE (Adaptive Query Execution) para otimizar joins automaticamente ou aplicar técnicas de salting para redistribuir chaves problemáticas.

Cache e persistência de dados

O uso de cache ajuda a evitar o reprocessamento de transformações custosas que são utilizadas várias vezes no mesmo pipeline.

  • Spark Cache: Armazena DataFrames na memória usando cache() ou persist(). Lembre-se que o cache é lazy e só é preenchido após a primeira ação.

  • Databricks Disk Cache: Utiliza o SSD dos nós para armazenar dados lidos do storage automaticamente. É altamente recomendado para clusters que realizam leituras repetitivas.

Se sua empresa enfrenta pipelines lentos no Databricks, problemas de spill ou custos elevados de processamento, nossos especialistas podem ajudar a otimizar sua arquitetura Spark. Fale com a nossa equipe e garanta a eficiência dos seus dados. ⬇️

Ilustração de um robô da BIX Tecnologia processando fluxos de dados e gráficos digitais, representando a governança de agentes de IA.

TL; DR Perguntas frequentes sobre otimização no Apache Spark

O que é o shuffle no Spark? É a redistribuição de dados entre os nós do cluster. Ocorre em operações como joins e agrupamentos, sendo uma das etapas mais lentas do processo.

Como a Spark UI ajuda a identificar problemas? Ela mostra visualmente se as tarefas estão equilibradas, se há excesso de uso de disco (spill) e quais estágios estão consumindo mais tempo.

Qual a diferença entre OPTIMIZE e Z-Order? O OPTIMIZE agrupa arquivos pequenos para reduzir a sobrecarga de leitura, enquanto o Z-Order organiza os dados dentro desses arquivos para acelerar filtros.

O que é o Adaptive Query Execution (AQE)? É uma funcionalidade do Spark 3 que ajusta o plano de execução em tempo real com base em estatísticas colhidas durante a rodada, melhorando a performance de joins.

**Quando devo aumentar a memória do cluster? ** Quando as otimizações de código e o ajuste de partições não eliminarem o spill para disco, indicando que a carga de trabalho exige mais hardware.

Quer agilidade na entrega de software na sua empresa?

Saiba como podemos resolver isso.

Fale com nossos especialistas

Receba uma proposta sem compromisso.

Time BIX