LISTA DE CONTEÚDO.

  1. INTRODUÇÃO
  2. O QUE É SPARK SQL?
  3. SPARK SQL – TABELAS, VIEWS E BANCO DE DADOS
    1. TABELAS GERENCIADAS E NÃO GERENCIADAS
  4. METADATA & CACHE TABLE
  5. CONCLUSÃO
  6. ASSINE A NEWSLETTER:
  7. SIGA NAS REDES SOCIAIS:

INTRODUÇÃO

Um dos recursos mais utilizados dentro do Spark, o SparkSQL permite manipular dataframes, tables e views utilizando a linguagem SQL.

Desenvolvido com base no HIVE, que até então, era o software mais utilizado para tratar Big Data com SQL, o SparkSQL veio com uma alternativa mais poderosa e simples para tratar dados, seja com SQL ou linguagem de programação.

SparkSQL foi escrito com o compilador nativo de SQL e teve o HIVEQL acoplado em seu engine, o que permite utilizar tanto o SQL-ANSI quanto HIVE queries.

Desenhado para rodar em ambientes OLAP, o SparkSQL, com toda certeza, é de grande valia em seus workloads.

Assim sendo, vamos ao artigo e entender como funciona e quais seus recursos.

O QUE É SPARK SQL?

Spark SQL é um recurso implementado no framework que permite utilizar comandos SQL nas fontes de dados importadas para a ferramenta. Bem como dataframes, fontes externas, tabelas, views e etc.

A tecnologia permite que utilizemos funções de sistemas ou funções definidas pelo usuário (UDF), analisar os planos de execução de query e otimizar os workloads.

No final, podemos tanto manipular os dados com SQL ou com dataframes que o código será compilado da mesma forma.

O Spark SQL é um componente do Spark que integra processo relacional com as funções da programação funcional via API do Spark.

NOTA: se olharmos com atenção, é o que ocorre com PySpark, que dentro da biblioteca, possui diversas funções SQL e até roda comandos SQL.

Como o Spark SQL tem uma forte ligação com o HIVE, é possível utilizar o Metastore para extrair informações das tabelas e colunas utilizadas na query executada com Spark SQL.

Como dito no primeiro parágrafo desta seção, Spark SQL integra processamento relacional com programação funcional. A prova disso podemos ver no breve exemplo abaixo:

Utilizando um SELECT com o método .sql no dataframe.
Utilizando um SELECT com o método .sql no dataframe.

Para começar a manipular um dataframe utilizando a linguagem SQL, primeiro precisamos criar uma VIEW (saiba mais sobre views em sql aqui.) temporária para então, utilizarmos desse recurso.

Spark não permite que executemos um comando SQL direto no dataframe.

Criando um dataframe e utilizando comando SQL para consulta.
Criando um dataframe e utilizando comando SQL para consulta.

Veja a mensagem de erro abaixo, informando que a tabela ou a view não existe no database do Spark.

Criando um dataframe e utilizando comando SQL para consulta.
Criando um dataframe e utilizando comando SQL para consulta.

Ao transformar o dataframe original em uma view dentro do Spark, conseguimos acessar e executar consultas SQL.

Note que o dataframe já existe e o que eu executei foi uma função transformando-o em uma view temporária. Com a view criada, toda manipulação com a linguagem SQL é permitida

Transformando o dataframe em view e manipulando com SQL.
Transformando o dataframe em view e manipulando com SQL.

O interessante é a versatilidade. Um dataframe criado a partir de uma fonte se tornou uma view e essa view deu origem a outro dataframe para manipulação com PySpark.

Note que na imagem acima, minha consulta esta buscando dados na view criada e o resultado sendo passado para o dataframe.

O que facilita o tratamento de dados e o ajusta para onde formos mais proficientes.

Utilizando PySpark no dataframe criado a partir da view.
Utilizando PySpark no dataframe criado a partir da view.

Uma vez passado para o dataframe, o manejo dos dados na tabela passa a ser híbrido.

Demonstração de versatilidade do dataframe com SparkSQL.

NOTA: é como se houvesse um aninhamento de comando. Por dentro, como a view existe, estou utilizando a linguagem SQL e como estou atribuindo o resultado da consulta para um dataframe, posso utilizar  PySpark.

Como todas as queries executadas com Spark SQL retornam um dataframe, acabamos por poder manusear os dados também com PySpark.

Uma situação curiosa me aconteceu enquanto testava queries SQL no Spark. Ao utilizar Window Function(aqui e aqui), o Spark não processa com boa performance queries que não possuam o partition by na cláusula.

Alerta de performance quando utilizamos Window Function
sem particionamento.
Alerta de performance quando utilizamos Window Function sem particionamento.

Com o particionamento, não houve alerta de performance por parte do Spark.

Window Function criada com Partition by na cláusula. 
Não há queda na performance.
Window Function criada com Partition by na cláusula. Não há queda na performance.

O interessante do SparkSQL é a capacidade de transformação e criação de análises que temos, podendo trabalhar amplamente com CTE, CASE e etc.

Uso de CASE na manipulação de dados.
Uso de CASE na manipulação de dados.

Veja o exemplo com uso de CTE.

Utilizando CTE e CASE na manipulação de dados e queries.
Utilizando CTE e CASE na manipulação de dados e queries.

Pensando na forma como o SparkSQL sustenta a execução das API’s de dataframe e dataset, bem como as linguagens que manipulam, veja que os dois códigos abaixo entregam o mesmo resultado.

SPARK SQL – TABELAS, VIEWS E BANCO DE DADOS

Sabemos que as tabelas e views, tanto no Spark quanto em qualquer outro local, são formas de apresentar os dados de maneira estruturada e organizada por colunas.

Embora similares, os dataframes são definidos dentro do escopo da linguagem que estamos utilizando com Spark, diferente das tabelas que estão no contexto do banco de dados.

Spark permite criar databases em seu ambiente e manipular suas tabelas, assim como as views que criamos anteriormente.

Importante diferenciar que no ambiente Spark, tabelas sempre possuem dados, diferente das views temporárias, que por vezes, podem não ter.

TABELAS GERENCIADAS E NÃO GERENCIADAS

Sabemos que as tabelas e views, tanto no Spark quanto em qualquer outro local, são formas de apresentar os dados de maneira estruturada e organizada por colunas.

Criação de um database com SQL no Spark.
Criação de um database com SQL no Spark.

O Spark em particular, permite duas instâncias de tabelas:

  • Gerenciadas → Spark gerencia os dados e metadados em algum sistema de armazenamento. Seja ele local ou cloud.
  • Não Gerenciadas → Spark gerencia apenas os metadados. O gerenciamento dos dados da tabela fica por conta do usuário em alguma fonte externa.

Tabelas  não gerenciadas são criadas quando utilizamos arquivos externos ou no próprio disco. Caso criemos tabelas a partir de um dataframe, criaremos tabelas gerenciadas.

É importante que ao criar a session que utilizará bancos do Spark, dê um diretório para os dados. Do contrário, ele utilizará um diretório padrão.

Informando o diretório do database ao criar a session do Spark.
Informando o diretório do database ao criar a session do Spark.

Tanto a tabela gerenciada quanto a não-gerenciada são possíveis de saber onde foram gravadas pelo Spark. Mas é preciso ressaltar que há uma pequena diferença no processo.

Tabela gerenciada: Veja que imprimo o diretório que o warehouse está armazenado e a tabela gerenciada se encontra no mesmo local.

Diretório da tabela gerenciada no Spark. 
Perceba que o local é o mesmo do DW, utilizando um esquema de pasta e subpasta.
Diretório da tabela gerenciada no Spark. Perceba que o local é o mesmo do DW, utilizando um esquema de pastas e subpastas.

Tabela não-gerenciada:  o diretório que é exibido é a fonte do arquivo utilizado para criar a tabela.

Diretório da tabela não gerenciada. Ele aponta para 
a fonte de dados, não para o path do warehouse.
Diretório da tabela não gerenciada. Ele aponta para a fonte de dados, não para o path do warehouse.

No explain, temos o resultado apontando para o diretório que os arquivos de fonte estão.

NOTA: em tabelas gerenciadas, comandos como DROP TABLE deletam tanto os dados quanto os metadados da tabela criada. Diferente das tabelas não gerenciadas pelo Spark.

A partir do momento que criamos uma tabela gerenciada, Spark irá manter os dados no local de armazenamento.

Para manipular as tabelas, primeiro criamos o banco de dados, o dataframe, salvamos no banco de dados e executamos consultas e manipulações.

E como sabemos se estamos criando uma tabela gerenciada ou não-gerenciada?

A criação de uma tabela gerenciada ou não vai variar do método utilizado quando estamos trabalhando com fontes de dados.

É possível criar tanto com linguagem SQL normal ou utilizando as API’s do Dataframe. PySpark, Scala ou R.

Na primeira “etapa”, criei o banco e o dataframe que servirá para criar a tabela.

Criação do database e dataframe para criar a tabela.
Criação do database e dataframe para criar a tabela.

Reparem que a estrutura funciona como se estivéssemos trabalhando com um banco de dados. Todo o fluxo é realmente muito similar.

Utilizando o Dataframe API, vejam que quando fui criar a tabela utilizo o método saveAsTable. Faço uso do write antes, indicando para o Spark escrever esses dados no banco.

Note que no diretório do Spark, os dados foram gravados como parquet, divididos em diversos blocos.

Filizando o processo anterior, criando a tabela no DW do Spark.
Filizando o processo anterior, criando a tabela no DW do Spark.

Outra maneira de criar tabelas gerenciadas no Spark, como dito acima, é utilizando o SparkSQL junto com comandos SQL.

Banco devidamente criado e resultado da query na tabela.
Banco devidamente criado e resultado da query na tabela.

Um outro exemplo exemplificando com um INSERT arbitrário.

Criando uma tabela e inserindo dados com SparkSQL
Criando uma tabela e inserindo dados com SparkSQL.

Como estamos criando uma tabela gerenciada, veja que ela está salva no meu diretório do Spark.

Exibindo o resultado da consulta.

Resultado da consulta na tabela criada.
Resultado da consulta na tabela criada.

Já as tabelas não gerenciadas, utilizamos uma fonte externa no SparkSQL declarando-a com o USING e informando o PATH de origem da fonte de dados.

Criando uma tabela não gerenciada com uma fonte externa.
Criando uma tabela não gerenciada com uma fonte externa.

E como estamos lidando com tabela não-gerenciada, repare que ela não está armazenada no database do Spark.

Como estamos utilizando SELECT e banco de dados, se quisermos realizar uma operação de BULK, um SELECT INTO por exemplo, o Spark permite.

Utilizando SELECT INTO para criar e popular uma tabela.
Utilizando SELECT INTO para criar e popular uma tabela.

Utilizando PySpark para criar uma tabela gerenciada.

Criando uma tabela gerenciada com PySpark.
Criando uma tabela gerenciada com PySpark.

Ainda sobre as tabelas “não-gerenciadas”, caso seja necessário ter o diretório da tabela exibido no sistema, podemos utilizar o recurso do ‘external table’ apontando para uma pasta específica e criá-la a partir do comando ‘create external table’.

Criando uma tabela não gerenciada com SQL.
Criando uma tabela não gerenciada com SQL.

Mais uma vez, exibindo o plano com o explain, veja como o Spark processa o primeiro trecho do comando, que cria a tabela:

NOTA: a leitura do physical plan parte debaixo para cima.

Plano de execução do Spark para criar uma tabela N-Gerenciada.
Plano de execução do Spark para criar uma tabela N-Gerenciada.

Primeiro o Spark irá scanear os arquivos parquet que foram utilizados para criar a tabela. Mesmo criando a partir do SELECT, como a fonte de dados é de um arquivo externo, ele realizará essa tarefa.

Como parquet é um formato colunar, ele transforma esses dados em  linha para armazenar os dados em tabelas.

Por fim, cria a tabela no banco e escreve os dados.

O interessante é que podemos criar tabelas particionadas, como se estivéssemos utilizando Window Function(aqui e aqui).

Utilizando o PARTITION BY na cláusula do SELECT. 

Criando tabelas particionadas a partir de um dataframe.
Criando tabelas particionadas a partir de um dataframe.

Ao final do processamento, teremos uma tabela com seus arquivos particionados de acordo com a coluna informada na query de criação.

O único problema é o processo de criação mais lento. Cuidado.

Perceba que demorou cerca de 1:30m para um dataset pequeno. 

De uma tabela N-Gerenciada, é possível criar uma gerenciada e particionada.
De uma tabela N-Gerenciada, é possível criar uma gerenciada e particionada.

Caso queira criar uma tabela particionada, utilize views como solução. Agora, se for de extrema necessidade, crie uma tabela física.

Criando uma view particionada para obter performance.
Criando uma view particionada para obter performance.

METADATA & CACHE TABLE

Sabemos que monitorar os metadados das tabelas é de extrema importância e com Spark SQL, temos  opção de utilizar uma query para realizar essa operação.

O interessante é que esse é o melhor exemplo de uma manipulação híbrida. Veja que no mesmo bloco que utilizo comando SQL, também utilizo recursos da API de Python.

Obtendo metadata das tabelas criadas no banco de dados do Spark.
Obtendo metadata das tabelas criadas no banco de dados do Spark.

Como extra, alguns comandos de ‘manutenção’ das tabelas Spark.

Comandos para atualizar e realizar manutenção em tabelas do Spark.
Comandos para atualizar e realizar manutenção em tabelas do Spark.

Além dos metadados, é possível colocar tabelas em caches, como os dataframes. O processo é bem simples, bastando utilizar o comando SQL.

Claro que quanto maior a tabela, mais demorado é o processo.

Colocando tabelas em cache com comandos SQL.
Colocando tabelas em cache com comandos SQL.

Também é possível pôr em cache uma tabela durante sua criação. Basta utilizar a função cache() ao final do comando de criação.

Utilizando PySpark na criação da tabela com SQL para deixá-la em cache.
Utilizando PySpark na criação da tabela com SQL para deixá-la em cache.

E para consultar quais objetos existem no database criado no Spark, utilize o comando abaixo:

Consultando as views e tabelas do banco de dados.
Consultando as views e tabelas do banco de dados.

Ele ainda informa se o objeto é temporário ou não. Ajudando muito na manutenção.

CONCLUSÃO

Neste artigo introdutório quis mostrar como o SparkSQL funciona e as possibilidades do recurso no tratamento e manipulação de dados.

O interessante e que me chamou muita atenção foi a versatilidade em que podemos lidar com os dados. Tanto com PySpark ou SQL, o framework é capaz de resolver e entregar as necessidades de forma correta e confiável.

Outro ponto que chama atenção é que uma vez que a view esteja criada, as possibilidades de operações se tornam quase ilimitadas.

É preciso ter atenção quando for trabalhar com views no Spark. Existem dois tipos Global e local. Quando uma local é criada, ela se torna ‘fixa’, o que impede a recriação como ocorre quando utilizamos createOrReplaceGlobalTempView().

Alguns alertas podem ocorrer durante seu estudo por causa das configurações do HIVE. Futuramente pretendo abordar esse assunto e explorar as configurações para otimizar o Spark.

E o que pude perceber é que não existe um método melhor para tratar dados no Spark. Vai muito da criatividade e conhecimento do profissional.

Na parte dois irei aprofundar o assunto trazendo novos conceitos e mais usabilidade.

Baixe os scripts utilizados AQUI.

Se quiser acessar um mapa mental com as funções PySpark, CLIQUE AQUI.

ASSINE A NEWSLETTER:

Fique atualizado de todos os artigos que saem no blog! Assine abaixo!

Não se esqueça de curtir e compartilhar em sua rede! Ajuda muito!

SIGA NAS REDES SOCIAIS:

Publicidade