Os problemas de desempenho mais comuns ao usar o Spark — Parte 2.

Juan Caio
3 min readFeb 14, 2023

--

Imagem de rawpixel.com no Freepik

No meu último artigo, eu escrevi um pouco sobre os principais problemas de desempenho ao usar o Spark, bom esse artigo aqui será uma continuação do primeiro, nessa parte 2, vamos tratar sobre o que podemos fazer para evitar ou diminuir esses problemas.

Spill

O que podemos fazer para mitigar o Spill ?

  • A maneira mais fácil e cara é alocar um cluster com mais memória por worker.
  • Em caso de Spill causado por desvio, trate o desvio e o Spill será resolvido.
  • Diminuir o tamanho de cada partição aumentando o número de partições, e como podemos fazer isso?

=> Gerenciando spark.sql.shuffle.partitions.

=> Ao repartir explicitamente.

=> Gerenciando spark.sql.files.maxPartitionBytes.

Para saber mais sobre esse

Skew

O que podemos fazer para mitigar o Skew?

  • O primeiro problema que precisamos resolver é a distribuição desigual de registros em todas as partições, o que resulta em tarefas mais lentas.
  • Mitigar o problema de RAM que causa erros de spill ou falta de memória pode apenas corrigir os sintomas, mas não a causa raiz.

Estratégias para corrigir o skew:

  • Ative a execução da Adaptive Query se estiver usando o Spark 3, que equilibrará as partições para os nós automaticamente, o que é um recurso muito bom do Spark 3.
  • Empregar uma skew-hint. Com as informações dessas hints, o Spark pode construir um plano de consulta melhor, que não sofra de distorção de dados.
  • Salt a coluna distorcida com um número aleatório, criando uma melhor distribuição em cada partição com o custo de processamento extra.

Aqui você pode ler mais sobre o assunto: https://www.databricks.com/blog/2020/05/29/adaptive-query-execution-speeding-up-spark-sql-at-runtime.html

https://docs.databricks.com/optimizations/skew-join.html

Shuffle

O que podemos fazer para atenuar os problemas de desempenho causados ​​pelo Shuffle?

  • Nossa estratégia número um é reduzir o I/O de rede usando menos e mais workers. Quanto maior a máquina e quanto menos máquinas tivermos, menos dados teremos para mover entre essas máquinas. Ainda incorreremos no I/O do disco, mas com menos máquinas, reduziremos significativamente o I/O da rede.
  • A próxima estratégia é reduzir a quantidade de dados sendo embaralhados como um todo. Algumas das coisas que podemos fazer são: livrar-se das colunas que você não precisa, filtrar registros desnecessários, otimizar a ingestão de dados.
  • Desnormalize os conjuntos de dados especificamente se o shuffle for causado por uma join.
  • Se você estiver unindo tabelas, poderá empregar um BroadcastHashJoin, caso em que a menor das duas tabelas será redistribuída para os executores para evitar totalmente a operação de shuffle. Isso é controlado pela propriedade spark.sql.autoBroadcastJoinThreshold (a configuração padrão é 10 MB). Se a menor das duas tabelas atingir o limite de 10 MB, poderemos transmiti-la.
  • Para joins, embaralhe previamente os dados com um conjunto de dados agrupado. O objetivo é eliminar a troca e classificação pré-embaralhando os dados. Os dados são agregados em N baldes e opcionalmente classificados e o resultado é salvo em uma tabela e disponível para leituras subsequentes.

Aqui voce pode ler mais sobre o assunto:

https://kb.databricks.com/data/bucketing.html

Storage

  • Evitar manipulação de arquivos de partição com menos de 128 MB.
  • Ao varrer diretórios, podemos ter uma longa lista de arquivos em um único diretório ou, no caso de conjuntos de dados altamente particionados, pastas de vários níveis. Para reduzir a quantidade de leitura, podemos registra o DataFrame como uma tabela.
  • Dependendo do formato de arquivo usado, pode haver diferentes problemas de esquema. Por exemplo, usando JSON e CSV, todos os dados precisam ser lidos para inferir tipos de dados. Para o Parquet, apenas uma única leitura de arquivo é necessária, mas toda a lista de arquivos do Parquet precisa ser lida se precisarmos lidar com possíveis alterações de esquema ao longo do tempo. Para melhorar o desempenho, pode ser útil fornecer definições de esquema com antecedência.

Serialization

  • Problemas de serialização podem surgir ao integrar bases de código com sistemas legados (por exemplo, Hadoop), bibliotecas de terceiros e estruturas personalizadas. Uma abordagem importante que podemos adotar para reduzir os problemas de serialização é evitar o uso de UDFs ou UDFs vetorizados (que agem como uma caixa preta para o Catalyst Optimizer).

--

--