Otimizando o desempenho no PySpark com com arquivos Parquet - Parte I

Otimizando o processamento de Big Data: uma análise aprofundada dos métodos de codificação Parquet.

🕒 Tempo estimado de leitura: 8 minutos

Parquet é um formato de arquivo de armazenamento em colunas otimizado para uso com estruturas de processamento de big data como o Apache Spark. Com seus recursos de armazenamento eficientes, o Parquet desempenha um papel crucial na melhoria do desempenho, especialmente ao lidar com grandes conjuntos de dados.

Alguns dos recursos de destaque dos arquivos Parquet é o uso de técnicas avançadas de codificação, como Bit Packing, Dictionary Encoding e Run-Length Encoding (RLE), além do uso de algoritmos de compressão que são essenciais para aumentar o desempenho e reduzir o espaço de armazenamento.

Neste e no próximo texto, exploraremos esses conceitos em profundidade. Mas antes de nos aprofundarmos nas técnicas de codificação, vamos entender brevemente o armazenamento em linhas para então entender sobre armazenamento em colunas.

Armazenamento baseado em linhas

O armazenamento baseado em linha (também conhecido como armazenamento orientado a linha) organiza dados por linhas. Cada linha armazena um registro completo, e cada registro inclui todos os campos (ou colunas) desse registro. Esse formato é típico em bancos de dados relacionais tradicionais como MySQL e PostgreSQL.

O formato de linhas.

No armazenamento baseado em linhas, cada registro (ou linha) é armazenado junto, então, quando você lê ou grava um registro, você acessa todas as colunas dessa linha simultaneamente.

O formato de linhas.

Vantagens do armazenamento baseado em linha
  • Eficiente para operações transacionais: o armazenamento baseado em linha é otimizado para operações orientadas a transações, nas quais você frequentemente precisa acessar e modificar registros inteiros, como inserir novos detalhes do cliente ou atualizar um pedido.

  • Acesso simples a dados: o acesso a um registro completo é direto, facilitando o manuseio de operações como adicionar ou atualizar linhas.

Desvantagens do armazenamento baseado em linha
  • Ineficiente para consultas analíticas: ao executar consultas analíticas que exigem apenas colunas específicas, o armazenamento baseado em linha pode ser ineficiente, pois lê linhas inteiras, incluindo dados desnecessários.

  • Espaço de armazenamento: o armazenamento baseado em linha pode usar mais espaço se não for otimizado corretamente, pois linhas inteiras são armazenadas juntas, incluindo quaisquer campos de dados não utilizados ou redundantes.

Em resumo, este formato é intuitivo e funciona bem ao acessar registros inteiros com frequência. No entanto, pode ser ineficiente ao lidar com análises, onde você geralmente precisa apenas de colunas específicas de um grande conjunto de dados.

Por exemplo, imagine uma tabela com 50 colunas e milhões de linhas. Se você estiver interessado em analisar apenas 3 dessas colunas, um formato por linha ainda exigiria que você lesse todas as 50 colunas para cada linha.

Exemplo de formatos de arquivo baseados em linha: CSV, JSON, AVRO etc.

Armazenamento baseado em colunas

Ao contrário dos formatos baseados em linhas, o armazenamento em colunas salva os dados coluna por coluna, o que melhora o desempenho para consultas analíticas, pois permite que você leia apenas dados relevantes.

O formato em colunas.

Quando os dados são armazenados usando uma abordagem baseada em colunas, pode haver algumas desvantagens. Um grande desafio é que quando você precisa gravar ou atualizar dados, isso afeta várias partes das colunas, levando a inúmeras operações de entrada/saída. Isso pode tornar o processo significativamente mais lento, especialmente ao lidar com grandes volumes de dados.

Além disso, se uma consulta precisar de informações de várias colunas, o sistema deve reunir os registros dessas colunas diferentes e combiná-los. Quanto mais colunas sua consulta envolver, mais complexa e exigente essa tarefa se tornará.

O lado positivo é que usar um formato de armazenamento híbrido combina os melhores aspectos das abordagens tradicionais de armazenamento baseado em linhas e armazenamento em colunas. Esse método híbrido pode oferecer desempenho e flexibilidade aprimorados, tornando-o uma escolha prática para lidar com uma variedade de necessidades de armazenamento de dados.

Formato Parquet

Visão geral

Apache Parquet é um formato de arquivo de armazenamento em colunas especificamente otimizado para uso com estruturas de processamento de big data, como Apache Hadoop e Spark. O Apache Parquet foi lançado pela Apache Software Foundation em 2013. Ele ganhou força relativamente rápido devido à sua eficiência e compatibilidade com as necessidades de processamento de big data.

O desenvolvimento do Parquet foi iniciado por engenheiros do Twitter e da Cloudera, com contribuições importantes de Julien Le Dem do Twitter e Todd Lipcon da Cloudera.

O Parquet foi criado para ser compatível com várias ferramentas e linguagens de processamento de dados, predominantemente dentro do ecossistema Hadoop, mas também além. Ele suporta o sistema de serialização Avro, tornando-o flexível para uso em diversos sistemas e cenários.

O formato também suporta a evolução do esquema, tornando mais fácil lidar com mudanças na estrutura de dados ao longo do tempo sem quebrar a compatibilidade ou exigir esforços extensivos de migração de dados.

Devido à sua natureza colunar, o Parquet suporta recursos como pushdown de predicado, permitindo que mecanismos de consulta pulem blocos irrelevantes de dados rapidamente.

Com esses recursos, o Parquet se tornou um padrão para armazenar e processar conjuntos de dados grandes e complexos, especialmente em setores e campos onde as ferramentas de big data são predominantes. Sua popularidade continua crescendo devido à sua eficiência e adaptabilidade em vários ambientes, incluindo arquiteturas de data lake baseadas em nuvem.

Sobre o formato

Os arquivos Parquet organizam os dados em "grupos de linhas", cada um representando um segmento do conjunto de dados geral. Pense nisso como dividir os dados em seções por linhas, o que ajuda a estruturar os dados de forma eficiente. Dentro de cada grupo de linhas, os dados são divididos por colunas no que é conhecido como "blocos de colunas". Esse processo atua como um particionamento vertical dos dados.

É importante observar que todos os blocos de colunas dentro de um grupo de linhas são armazenados juntos sequencialmente no disco de armazenamento, garantindo que permaneçam como uma unidade coesa.

Arquivo Parquet.

Você pode presumir, como eu inicialmente presumi, que o Parquet é apenas um formato de armazenamento em colunas. No entanto, isso não é totalmente verdade. O Parquet emprega um modelo híbrido para armazenamento de dados.

Parquet Encoding

A codificação Parquet torna o tamanho dos dados menor, portanto o volume de IO é menor e o uso de memória é menor. Apesar dos custos extras de CPU serem necessários para decodificar os dados Parquet, ainda é um bom negócio negociar custos de CPU por custos de IO.

Para computadores modernos, é muito mais barato aumentar o poder de computação em comparação a aumentar o desempenho de IO. Especialmente para carga de trabalho de big data, IO é o gargalo do desempenho da consulta de dados.

Algoritmos de codificação em Parquet

Ao lidar com armazenamento de dados no formato Parquet, é importante entender os vários algoritmos de codificação usados ​​para otimizar o armazenamento e a recuperação de dados. Vamos analisar algumas técnicas de codificação importantes usando exemplos simples. Aqui usarei um conjunto de dados simples como exemplo para explicar os algoritmos de codificação.

Conjunto de dados simples como exemplo.

Codificação de dicionário

Pense na codificação de dicionário como uma tabela de consulta exclusiva para dados em uma coluna. Imagine que você tem uma coluna para nomes de "Produtos". Com a codificação de dicionário, cada nome de produto distinto é armazenado apenas uma vez em uma lista separada, chamada de dicionário "Produto", e cada um recebe um índice exclusivo. Em vez de armazenar o nome completo do produto repetidamente, a coluna agora contém apenas os valores do índice.

Codificação de dicionário.

  • Desempenho: quanto menos valores exclusivos (menor cardinalidade) uma coluna tiver, melhor esse método compacta. Por exemplo, uma coluna "Gênero", que normalmente tem menos valores exclusivos, compacta melhor do que uma coluna "Código postal".

  • Consistência da consulta: independentemente do tipo de dados (por exemplo, texto, números), uma vez codificado, o desempenho da consulta permanece consistente porque os valores de índice são uniformemente dimensionados.

Codificação de comprimento de execução (RLE - Run-Length Encoding)

RLE é como uma abreviação para dados repetidos. Para uma coluna como "Quantidade", onde os valores podem se repetir em sequência, RLE compacta os dados armazenando o valor uma vez e contando quantas vezes ele se repete consecutivamente.

Codificação de comprimento de execução (RLE).

  • Eficiência com padrões: alta repetição significa melhor compactação. Por exemplo, se várias linhas em "Quantidade" tiverem o mesmo número repetido, RLE pode reduzir drasticamente o tamanho do armazenamento.

  • Benefício da classificação: classificar os dados primeiro pode melhorar muito a compactação, pois RLE prospera em valores idênticos consecutivos.

  • Limitações: colunas com valores em constante mudança não são ideais para RLE, pois podem até aumentar o tamanho dos dados.

Combinando RLE e codificação de dicionário

Esses dois métodos podem trabalhar juntos. Depois de usar a codificação de dicionário para converter valores em índices numéricos, o RLE pode compactá-los ainda mais se houver um padrão de repetição.

Codificação Delta

A codificação Delta é como uma sequência de pequenos passos em vez de saltos completos. Em vez de armazenar valores de dados inteiros, ela registra a diferença entre cada valor consecutivo. Isso é especialmente útil para dados que mudam ligeiramente ao longo do tempo, como dados de séries temporais.

Delta Encoding.

  • Ideal para pequenas variações: quando os valores mudam incrementalmente, como carimbos de data/hora registrados em segundos, a codificação delta reduz significativamente as necessidades de armazenamento. Apenas as pequenas diferenças são salvas, não os valores completos.

Conclusão

Em resumo, o Parquet usa essas estratégias de codificação para otimizar o armazenamento e o processamento de grandes conjuntos de dados. Cada técnica tem seus pontos fortes, dependendo da natureza dos dados, e pode impactar significativamente o desempenho e a eficiência nas operações de dados.

Embora o design em colunas do Parquet forneça inerentemente benefícios como melhoria de desempenho e redução de armazenamento, nos próximos textos vamos explorar como estas técnicas combinadas com algoritmos de compressão e recursos de particionamento podem levar a eficiência do Parquet um nível mais alto.