BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias Trabalhando com Apache Spark em escala

Trabalhando com Apache Spark em escala

Favoritos

Holden Karau, Principal Software Engineer do IBM's Spark Technology Center e commiter do Apache Spark, fez uma análise do Apache Spark tendo como foco seu desempenho ao lidar com grandes conjuntos de dados. Nessa análise, ela aborda a abstração para os dados, a computação distribuída que o Spark Core utiliza, e a reutilização de dados feita pelo Spark. A apresentação aconteceu na a 12ª edição da Emerging Technologies Conference, na Filadélfia.

Spark é um sistema distribuído, um projeto Apache que visa velocidade e facilidade de uso e análise para grandes quantidades de dados. Possui um bom desempenho com grandes quantidades de dados apenas uma máquina para processá-los. Existem duas abstrações para os dados distribuídos em Spark: RDD (Resilient Distributed Dataset) e DataSet. O RDD é como uma coleção, suporta muitas das mesmas operações que Seq's no Scala, que é distribuído automaticamente e tolerante a falhas. Os DataFrames (coleções distribuída de dados organizados em colunas nomeadas) podem ser construídos a partir de uma ampla variedade de fontes, tais como: arquivos de dados estruturados, tabelas em Hive, bancos de dados externos ou RDDs existentes. O Dataset é uma interface fornece os benefícios de RDDs junto com os benefícios do mecanismo de otimização da Spark SQL. Um Dataset pode ser construído a partir de objetos JVM e depois manipulado usando transformações funcionais.

Fonte da Imagem: https://www.infoq.com/presentations/scale-apache-spark?utm_source=presentations_about_bigdata/utm_medium=link/utm_campaign=bigdata

Karau divide o projeto em 3 partes: a primeira parte chamada de Lazyness, a segunda parte o particionamento, e a terceira e última as funções arbitrárias.

Na primeira parte, RDD e DataFrames são apenas "planos" de como fazer com que os dados sejam apresentados forçando o trabalho do Spark. Os dados não existem até que sejam necessários. Karau explica:

Você me pergunta se eu poderia fazer um Map e um FlatMap e algumas outras coisas no Spark, mas eu posso combinar tudo isso em tarefas.

Os planos podem ser realizados de duas formas: através de DAG (Directed Acyclic Graph - Grafo acíclico dirigido) ou através da Query Plan (plano de consulta). DAG é constituído de uma lista de tarefas a serem realizadas podendo ser de forma aleatória, pois chega a um estágio em que elas não podem ser realizadas localmente, mas possuem dependências entre uma e outra. O Query Plan, é um plano mais elaborado podendo realizar as tarefas de forma mais otimizada.

De forma resumida, nesta primeira etapa é possível realizar pipelining (podendo colocar maps, filter, flatMap todos juntos), realizar otimizações postergando trabalhos e utilizar o DAG para recalcular o fracasso. Mas, para debugar é confuso e para reutilizar os dados a "preguiça" vê apenas a primeira opção dos dados, ignorando o resto.

Se você já sabe que irá reutilizar o RDD, é possível adotar a persistência dos dados. A persistência pode ser feita por MEMORY, MOMORY_ONLY_SER, MEMORY_AND_DISK, MEMORY_AND_DISK_SER. Caso os dados sejam de grande valia é possível armazená-los em duas máquinas diferentes.

A segunda parte é o particionamento, ou seja, quando se necessita obter dados para diferentes máquinas. Para esses casos é utilizado um compartilhamento especial. Os particionamentos em Spark são determinísticos na chave de entrada (por exemplo: para uma chave dada, essa chave deve sempre ser enviada à mesma partição). As operações de impacto são groupByKey, ou também sortByKey.

Existem key-skew que são chaves que não são distribuídas uniformemente e, então, o groupByKey poderá "explodir" e as partições não serão balanceadas. Para resolver esse problema, podemos utilizar o reduceByKey, que funciona quando os tipos são os mesmos, ou o aggregateByKey, que não requer que os tipos sejam os mesmos. Dessa forma, é possível permitir que o Spark encaminhe a chave e faça uma redução da lista. A utilização de partições determinísticas divide os dados e o trabalho, permite joins mais rápidos, e ainda é possível fazer pesquisas.

Na última e terceira parte, as funções arbitrárias são funções utilizadas para obter os dados, como o groupByKey + mapValues, mas também funcionam como um filtro. Essas funções possuem um alto grau de dificuldade para o otimizador. Com os Datasets, opções complementares ajudam na otimização. O groupBy retorna a GroupedDataStructure e oferece agregamentos especiais. Para isso serão utilizadas querys relacionais e funcionais juntas. Com isso, a execução dos dados é muito mais rápida como observado no gráfico abaixo:

Ainda assim, os DataFrames podem falhar com algoritmos iterativos, ou o tamanho do shuffle padrão é pequeno, ou o tamanho padrão da partição de leitura não é suficiente.

O Spark é feito em Scala, ou seja, roda na JVM. De toda forma, os usuários podem trabalhar em suas linguagens favoritas, como Python, R, C#, entre outras, sendo necessária a comunicação dessas linguagens com com a JVM. Para o Python Karau utiliza Py4J, que faz a comunicação com o Java para rodar o Spark. Os dados são armazenados na JVM, e então o Py4J faz a ligação com o Python para realizar a troca desses dados. A dupla serialização faz tudo custar mais caro, e o arranque do python leva um tempo extra, além de que por a memória do Python não ser controlada pela JVM torna-se mais fácil extrapolar os limites e os erros de mensagem fazem menos sentido.

Você pode conferir a apresentação completa realizada pela Holden Karau no InfoQ.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT