Entendendo o Big Query: Um aprofundamento no seu funcionamento interno

Matheus Tramontini
TC - Tecnologia e Produtos
6 min readFeb 3, 2023

Big Query é uma poderosa solução serverless de data warehouse disponibilizada no Google Cloud Platform. Ele permite que as organizações analisem grandes quantidades de dados com grande eficiência e velocidade, sendo junto com o Redshift uma escolha comum para negócios focados em dados.

Contudo, entender como ele realmente funciona por dentro pode ser um desafio tanto para pessoas novas no uso dele quanto para pessoas com mais experiência. Por conta disso, nesse artigo busco desmistificar como o Big Query funciona internamente e resumir de forma direta como ele consegue processar grandes quantidades de dados com facilidade. Espero que esse post ajude as pessoas a não se assustarem depois de ver por exemplo, a imagem abaixo, e a partir do entendimento de como os dados são processados, consigam tomar decisões técnicas com mais clareza na hora de montar a sua estrutura no Big Query.

Arquitetura interna do Big Query

Dremel

O Dremel nasceu na necessidade da Google de fazer os times internos analisarem massas de dados salvos em sistemas de arquivos distribuídos ou outros tipos de armazenamento (como por exemplo o BigTable) de forma rápida, sob demanda utilizando o mais próximo do SQL.

Mas o que o Dremel fez de diferente nesse caso? O primeiro ponto foi como ele faz para trabalhar com os dados não estruturados, utilizando o conceito de armazenamento colunar, onde os dados são lidos por colunas e não por linhas, isso já ajudava a diminuir a quantidade de MapReduce feito a cada consulta. Outro ponto importante foi o uso do conceito de árvores para a execução das queries permitindo assim não precisar recombinar os dados para trabalhar da forma convencional de um banco a cada chamada. E por fim o conceito de computação paralela utilizando diversos clusters compartilhados.

Agora, como último ponto do Dremel precisamos entender como ele utiliza o conceito de árvore para executar as queries. Primeiramente ele utiliza um “query dispatcher” para enviar a query para o nó raiz (root), o root tem duas funções principais, a primeira é enviar a informação para o próximo nível que é conhecido por conter os Mixers, a segunda função é retornar o resultado para o cliente que fez a requisição. Os Mixers vão reescrever a query para reduzir e otimizar aplicando principalmente as partições e shuffling, após isso vão enviar a query modificada para as folhas que vão executar o trabalho pesado, como a leitura dos dados no Colossus, aplicação de filtros e agregações, retiradas de colunas não utilizadas entre outras coisas, importante lembrar que cada folha tem seu processamento em slots (unidades de processamento) onde em média cada slot lê 5GB de dados.

Exemplo básico de processamento da query na árvore

Outro processo que o Dremel tem uso da estrutura de árvores é com os campos aninhados (nested fields). Nos campos aninhados o root é o maior nível e as folhas são as profundidades, ao executar a query o Dremel vai selecionar os campos da coluna que vai ser usado descartando os desnecessários atráves do conhecimento dos níveis da árvore tornando o processamento dessa estrutura mais rápida e leve.

Exemplo de comportamento da árvore para tratar campos aninhados

Caso tenha curiosidade e curta ler a parte mais teórica leia o paper oficial sobre o Dremel que é bem bacana.

Capacitor

É um sucessor do ColumnarIO que também utiliza dados colunares, mas com uma melhoria para realizar operações em arquivos comprimidos sem a necessidade de descomprimir-los.

Antes de seguir explicando como o Capacitor faz isso queria citar duas vantagens que eu acho importante para entender o porquê do formato colunar.

  • Scans Reduzidos: No formato colunar como os valores tem como referência a coluna não é necessário fazer o scan de todas as linhas com todas as colunas da tabela, mas apenas da colunas selecionada.
  • Melhor taxa de compressão: O armazenamento colunar consegue uma taxa de compressão de 1:10 enquanto os armazenamentos baseados em linhas conseguem aproximadamente 1:3.

A Google criou o Capacitor como um formato colunar interno que aplica fortemente variações e melhorias dos métodos de compressão e descompressão apresentados nessa pesquisa como Run Length Encoding (RLE), Dictionary encoding, etc..

Usando por exemplo o RLE como na imagem abaixo vamos precisar de cerca de 25 iterações para comprimir a informação.

Tabela sem ordenação

Porém se aplicarmos uma lógica e reordenação na tabela pode-se reduzir o número de iterações para 12 iterações melhorando o processo de armazenamento e processamento.

Tabela reordenada

Claro que internamente o Big Query aplica mais inteligência a essa ideia de compreensão, utilizando por exemplo modelos que diferenciam a melhor forma em caso de longs e strings, colunas que tem mais chance ser selecionadas no select ou filtradas numa cláusula where. Outro ponto interessante é que conforme ele aplica essa inteligência e regras ele vai coletando estatísticas únicas para cada colunas, assim podendo escolher o melhor caminho durante o planejamento da query, e no fim de todo processo ele persiste tanto essas informação quanto as tabelas comprimidas no Colossus.

Colossus

Colossus é o sistema de arquivos distribuídos da geração mais recente da Google (sucessor do Google File System), cada datacenter do Google tem seu próprio cluster do Colossus aplicando assim regras para lidar com replicação, recuperação de erros e gerenciamento distribuído.
Para reduzir o custo e facilitar a replicação entre cluster os dados são normalmente salvos utilizando Reed-Solomon e os metadados são salvos no banco NoSQL da Google BigTable, que aliás foi o que permitiu o Colossus escalar os clusters em até 100x mais que o GFS.

Borg

Para executar todo esse processamento o Big Query utiliza a estrutura do Borg que é um sistema de larga escala para gerenciamento de cluster que abstrai para os usuários partes complexas de escalabilidade de máquinas, processos de recuperação e manejo de erros. De forma simplificada, os usuários, nesse caso o Big Query, enviam cargas de trabalho em forma de jobs, cada um desses jobs vai ser executado em uma célula do Borg que é um conjunto de máquinas trabalhando como uma unidade.

A estrutura do Borg é tão escalável e funcional que foi utilizada como base para a criação do Kubernetes e mereceria um post só pra falar sobre todo o funcionamento dele.

Para quem tiver curiosidade é possível acessar o paper original e um outro paper com casos mais recentes de clusters do Borg.

Jupiter

E por fim para conseguir executar todas essas ferramentas e cargas de serviços o Big Query utiliza a rede da Google chamada Jupiter, que pode entregar cerca de 1 Petabit/segundo do total de banda em uma rede com bissecção. Para se ter ideia o total de rede utilizada em uma query rodando em uma estrutura de 100.000 máquinas de comunicando a 10 GBs seria cerca de 0,1% da capacidade total utilizando essa estrutura.

Esse post conta um pouco mais da ideia utilizada pelo Jupiter.

Conclusão

Espero que com esse post consigam entender melhor o funcionamento interno do Big Query e até adaptar formas de pensar na solução baseado nisso, eu mesmo já tive uma mudança na visão do funcionamento dele ao entender como todo esse conjunto funciona.

--

--