Apache Impala
Apache Impala
Apache Impala
RESUMO
1. APRESENTAÇÃO
2. FUNDAMENTAÇÃO TEÓRICA
2.1 AMBIENTE CLOUDERA QUICKSTART
2.2 LINGUAGEM C++
2.3 APACHE IMPALA
3. TRABALHOS RELACIONADOS
4. CONCEITOS SOBRE AS UDFS[15]
4.1 DECLARAÇÕES DDL – REGISTRO DA ASSINATURA DA UDF
4.2 ESTRUTURA DA UDF
5. IMPLEMENTAÇÃO
5.1 REQUISITOS
5.2 COMPILAÇÃO DA BIBLIOTECA COMPARTILHADA
5.3 INSTALAÇÃO DA BIBLIOTECA NO HADOOP HDFS
5.4 EXEMPLOS
5.4.1 EXEMPLO 1: RECUPERAÇÃO DE INFORMAÇÕES EM UMA LINHA DE LOG
5.4.2 EXEMPLO 2: RECUPERAÇÃO DA INFORMAÇÃO EM UMA LINHA DE LOG
USANDO THREADS
5.4.3 EXEMPLO 3: FUNÇÃO COM ARGUMENTOS DE COMPRIMENTO
VARIÁVEIS
5.4.4 REGISTRANDO AS ASSINATURAS DAS FUNÇÕES
5.4.5 APLICAÇÃO DAS FUNÇÕES
6. CONSIDERAÇÕES FINAIS E TRABALHOS FUTUROS
REFERÊNCIAS
APÊNDICE – REFERÊNCIAS DE NOTA DE RODAPÉ
RESUMO
Big Data nos lança constantes desafios técnicos voltados ao campo da infraestrutura
de processamento massivo de dados. Na tentativa de obtermos êxito nesta tarefa,
hardwares cada vez mais rápidos são utilizados, e consequentemente softwares
capazes de aproveitar estes recursos devem ser projetados. O Apache Impala é
resultado destes esforços possuindo um núcleo de processamento de alto
desempenho totalmente codificado na linguagem C++. Este artigo apresenta um
estudo introdutório sobre as técnicas para desenvolvimento das Funções Definidas
pelo Usuário (UDF), as quais tiram proveito dessa arquitetura permitindo que
algoritmos complexos possam ser escritos. As UDFs codificadas em C++ nos
permitem a criação destes algoritmos de maneira qual sejam otimizados tanto no
contexto da velocidade quanto do uso dos recursos computacionais disponíveis tais
como consumo de memória. No decorrer deste artigo apresentaremos as bases
teóricas necessárias para o entendimento da integração e funcionamento das UDFs
junto aos mecanismos do Impala. Encerramos com a demonstração por exemplos
das principais técnicas aplicadas a codificação na linguagem C++. Quando
completadas a fases do desenvolvimento, iremos registrar a função para uso no
contexto nativo do Impala, ou seja, na formação das declarações SQL desenvolvidas
pelos usuários.
1. APRESENTAÇÃO
Com o fenômeno Big Data a comunidade cientifica teve que repensar como os dados
deveriam ser tratados nos seus variados aspectos, criando e aprimorando recursos
de rede, armazenamento e processamento que garantam a segurança, integridade e
disponibilidade destes dados.
Conforme Taurion (2012), ”Big Data vem chamando atenção pela acelerada escala
em que volumes cada vez maiores de dados são criados pela sociedade.”. A
problemática que envolve o Big Data é ampla, sendo que para Taurion (2012), a
sustentabilidade do Big Data é vista sob ”[…] duas óticas: as envolvidas com
analytics, tendo Hadoop e MapReduce como nomes principais e a tecnologias de
infraestrutura[…]”, que são destinadas ao armazenamento e processamento de
dados. O Big Data possui muitas peculiaridades, uma das quais chama a atenção são
os dados que na sua maioria são semi estruturados ou desestruturados, não
seguindo os modelos tradicionais dos dados estruturados, pois apesar de ser
possível armazenar, por exemplo uma imagem em um campo, não se pode aplicar
as técnicas de ER[2] comuns para fazer associações à este objeto, daí a
necessidade da criação de sistemas de bancos de dados NoSQL[3].
O Apache Impala, foco deste trabalho, foi desenvolvido como sendo um sistema
para consulta e transformação de dados de alto desempenho que acessa dados
arquivados no HDFS usando a tradicional linguagem SQL. Suas características
apresentadas na Seção 2.3, o tornam ideal para o processamento de dados em
tempo real.
Nosso objeto de interesse é explorar a possibilidade de expansão das capacidades
de processamento de resultados do Impala mediante a programação de User-
Defined Functions (UDFs), utilizando a linguagem C++, que são executadas
diretamente nas declarações SQL do usuário do mesmo modo que as funções
nativas.
Este trabalho está assim estruturado: O Capítulo 2 são as bases teóricas necessárias
para alcançarmos nossos objetivos, apresentando as tecnologias utilizadas. No
Capítulo 3, é apresentado trabalhos relacionados os quais também foram fontes de
consulta. O Capítulo 4, abrange os conceitos aplicados as Funções definidas pelo
Usuário, com enfoque nas características básicas, operacionais e estruturais,
necessárias para que possamos compreender como elas funcionam internamente
para que possamos então codificá-las, também abrange as instruções DDL[5]
necessárias que são usadas para criação das funções no contexto do Impala. O
Capítulo 5 é a implementação da solução mediante exemplos. Por fim o Capítulo 6 é
o encerramento, no qual expomos nossas considerações sobre os assuntos
abordados e projetos futuros.
2. FUNDAMENTAÇÃO TEÓRICA
Fonte: Autor.
Outro fator que influencia diretamente na portabilidade do código executável refere-
se à representação binária dos dados e suas precisões.
Para termos melhores garantias de que não haverá problemas nesse sentido,
podemos utilizar tipos de dados definidos na STL para garantir a compatibilidade
binária entre sistemas.
Como exemplo dessa implementação o arquivo header cstdint define os tipos
inteiros portáveis conforme podemos observar no fragmento de código abaixo:
Desse modo quando queremos que haja garantias de que um número inteiro com
sinal tenha suas características binárias mantidas, devemos declará-lo do seguinte
modo: int64_t max_counter;
Pois caso ele seja declarado como um tipo ‘long long int’ poderá haver discrepância
em sua representação binária entre os sistemas.
Assim usando estas técnicas podemos criar programas realmente portáveis entre,
processadores de diferentes arquiteturas, sistemas operacionais e compiladores.
Preservando a principal característica da linguagem no que se diz respeito a geração
do código de máquina nativo. (STROUSTRUP, 2002)
Quando nos referimos ao Impala como “mecanismo de consultas” significa que ele
não é um SGBD[13] padrão como por exemplo o HBase que é um banco de dados
orientado a colunas ou o IBM® DB2® relacional. O Impala possui a capacidade ler
dados de diversos formatos tais como Parquet, Text, Avro, etc., que estão
armazenados no Hadoop HDFS, gerando resultados mediante o uso da linguagem
SQL. (KORNACKER et al., 2015)
Por fim temos as leituras diretas locais representadas pelo HDFS Namenode(NN) e o
banco de dados HBase. (IMPALA, 2020), (DATAFLAIR, 2020).
O Impala na sua estrutura possui dois componentes conhecidos
como frontend e backend, ao entendermos suas diferenças e como eles interagem,
vemos o quão pode ser apropriado a codificação em C++ das UDFs.
Análise da consulta;
Análise semântica;
Planejamento/otimização da consulta, sendo esta fase a mais
complexa.
O planejador de consultas do Impala recebe uma estrutura de dados em árvore,
juntamente com as informações globais da consulta reunidas durante a análise
semântica (identificadores de tabela/coluna, classes de equivalência, etc.). Um plano
de consulta executável é construído em duas fases: (1) planejamento de nó único e
(2) a paralelização e a fragmentação do plano., daí envia os fragmentos da consulta
para o que o backend possa executá-los.
Como nosso foco no Impala são as UDFs, seguiremos apresentando seu conceitos e
como elas se integram ao sistema.
3. TRABALHOS RELACIONADOS
Esta seleção de trabalhos relacionados nos mostram quão desafiador podem ser
nossos estudos sobre o tema proposto. As fontes de informação na sua maioria
utilizam as mesmas fontes de informações sendo as principais o próprio código fonte
do Impala e a documentação do site oficial e o artigo escrito por Kornacker al.
(2015).
Este trabalho escrito por Kornarcker et al. (2015), explica como o Impala presta os
serviços propostos ao usuário, contemplando todos os seus componentes e como
eles se integram. Explica em detalhes quais as funções dos chamados componentes
de backend e frontend e o uso da geração de código em tempo de execução.
Neste documento Infolab (2014), nos fala sobre como se considerou bem vinda a
capacidade do Impala em suportar UDFs, e principalmente em se poder além de
criá-las no formato de bibliotecas compartilhadas, que é possível usar código LLVM
IR e tirar vantagens desse recurso.
Do ponto de vista do usuário final, uma UDF não difere de uma função interna
padrão em nenhum aspecto, já que segue todas as mesmas regras de uso e
apresentação de resultados. Porém um aprofundamento nos conceitos estruturais
das UDFs são necessários quando nos dispomos a modificá-las.
Características primitivas:
Sintaxe Geral[18]:
Devemos notar que não é obrigatório que o nome da função que será visível ao
usuário do Impala seja o mesmo da função da biblioteca (SYMBOL). Isso é útil para
podermos dar nomes as funções mais sugestivos aos usuários. Quando damos o
nome da função temos também que indicar qual, ou quais são os tipos dos
argumentos de entrada indicados por seu parâmetro, não sendo necessário nomeá-
los, mas sim apenas colocar qual é seu tipo. Em seguida o parâmetro RETURNS
indicará qual é o tipo de dado esperado como retorno do processamento, no nosso
caso um tipo inteiro. O parâmetro LOCATION indica o caminho para o diretório
(pasta) onde o arquivo da biblioteca foi copiado para o HDFS e por último o
parâmetro SYMBOL sendo o nome interno usado na codificação da função.
Estrutura de
Função
Dados
struct Representa um atributo TIMESTAMP. Possui um membro de 32-bits que representa uma data Gregoriana e um tipo de 64-
TimeStampVal bits que representa a data corrente em nanossegundos.
API:
A comunicação da UDF com o restante sistema é feita através da
classe FunctionContext, tendo como funções examinar o estado do sistema,
reportar erros e gerenciar a memória.
Protótipo geral:
As UDFs devem ter seus protótipos definidos seguindo as seguintes regras:
Fonte: Autor.
A UDF com argumentos de comprimento variado, possui uma prototipação diferente
das com argumentos fixos, conforme o exemplo abaixo:
Fonte: Autor.
Durante a chamada da função o Impala se encarrega de passar os valores corretos
para num_var_args que é o total dos argumentos, sem contar o primeiro argumento
fixo separator e o contador de argumentos, além de um ponteiro para o primeiro
argumento da lista que será usado no código da função para os processamentos
necessários.
– Fechar (close): Esta função somente é chamada após todas as chamadas à UDF
principal terem se encerrado. Sua função é basicamente a de desalocar estruturas
de dados compartilhados para os quais os resultados não precisam ser mantidos. Do
mesmo modo que prepare, esta função deve ser definida na DDL CREATE
FUNCTION, usando close_fn = ’nome do símbolo’.
5. IMPLEMENTAÇÃO
5.1 REQUISITOS
API de apoio:
o h: Arquivo principal de acesso a API.
o h[20]:
Para que o Impala possa ter acesso à biblioteca, devemos copiá-la para o HDFS.
Como o HDFS não é um sistema de arquivos regular, não podendo ser acessado
diretamente, é necessário usar o comando hdfs do Hadoop na console do sistema
Linux conforme mostrado na Listagem 8.
5.4 EXEMPLOS
Para nossa conveniência, o formato da linha de log usado é do tipo CSV, outros tipos
de formato não são aceitos pela função.
Este exemplo é constituído das três funções as quais compõem a estrutura das
UDFs, sendo:
Função Preparação: Efetua as alocações de memória para a thread criada. Nos
permite fazer teste de integridade dos argumentos.
Esta função faz uso do mesmo algoritmo para “tokenizar” a linha do log usado na
função __squidLogParser() anterior.
Uma das características muito úteis das UDFs é a possibilidade de criarmos funções
de uso múltiplo, ou seja, dada uma única função, e passado um argumento de
controle podermos executar diferentes processos em seu interior, relacionados entre
si que façam uso dos mesmos argumentos de dados consumidos.
A função apresentada não preza pela sua usabilidade no mundo real, mas sim
somente pretende demonstrar ambos os conceitos discutidos, sendo: argumentos de
comprimento variados e múltiplo uso, efetuando cálculos estatísticos simples: Média
simples, Desvio médio, Variância e Desvio padrão.
Sintaxe:
Exemplo:
Argumentos de controle: M = Média; D = Desvio médio; V = Variância; P = Desvio
padrão.
Esta Seção destina-se a demonstrar como é feito o registro das assinaturas das
funções da biblioteca compartilhada.
Fonte: Autor.
A Figura 4, demonstra como devemos declarar a assinatura de função que suporta
argumentos com comprimento variáveis. Devemos notar que após o parâmetro
variável em quantidade devemos colocar asterisco como forma de indicar tal
característica.
Fonte: Autor.
A Figura 5, mostra o resultado de SHOW FUNCTIONS em forma de tabela. Os
campos possuem as seguintes designações:
Fonte: Autor.
Na Figura 6, usamos uma possível combinação de funções internas do Impala para
recuperar as informações referentes ao endereço IP do cliente e total de bytes da
requisição. A função split_part(), irá separar a linha de log mediante a informação
do tipo do separador dos campos utilizado, e retorna a parte da linha de log que
queremos mediante a informação numérica da posição do campo. O resultado dessa
função é uma string nesse formato por exemplo: “192.168.1.100”, notar as aspas
que fazem parte do resultado, para eliminarmos as aspas utilizamos a
função translate(), que efetua a troca das aspas por um caractere nulo. Para o total
de bytes é usada a mesma técnica, porém desejamos que o valor retornado seja
convertido em um tipo bigint, que para isso usamos cast(a as T).
Na Figura 7, efetuamos a mesma consulta, porém usando nossa UDF, podemos
notar que a declaração SQL fica com leitura e objetivos mais claros além de termos
que utilizar menos funções.
As UDFs, podem ser úteis tanto no processamento em lote com em tempo real,
neste último, seus benefícios podem ser aplicados na diminuição do uso das rotinas
de Serialização/Deserialização(SERDE) durante a coleta dos dados. Ao
simplificarmos os algoritmos de SERDE, podemos diminuir as latências envolvidas
nesse processo, dando ao coletor a oportunidade de consumir menos tempo na
sanitização dos dados de entrada melhorando sua capacidade de transmissão ao
destino quando este fator é crítico, a exemplo de IoT.
REFERÊNCIAS
BALENA, S. Creating UDF and UDAF for Impala. Hadoop Online Tutorial, 2016.
Disponivel em: <https://hadooptutorial.info/creating-udf-and-udaf-for-impala/>.
Acesso em: 15 jan. 2020.
2. Entidade Relacionamento.
6. Fonte:
<https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
#Rc-org>
18. Fonte:
<https://impala.apache.org/docs/build/html/topics/impala_create_function.html>
19. https://impala.apache.org/docs/build/html/topics/impala_double.html
[1]
Especialista em Business Intelligence e Big Data – Centro Universitário Padre
Anchieta Jundiaí; Tecnólogo em Redes de Computadores – Faculdades Anhanguera
de Jundiaí; Especialista em Sistemas Operacionais Unix/Linux; Especialista em
Linguagem C++; Especialista em Banco de dados – Impala, Hive e Hadoop HDFS.