Melhores práticas com Amazon Redshift: Arquitetura, organização e otimização de performance.

Um guia prático, baseado em experiências pessoais, que aborda a arquitetura, tipos de dados, compressão de colunas, estilo de distribuição e chave de ordenação do cluster Redshift, visando a melhora direta de performance.

Alice Thomaz
11 min readApr 24, 2023

Otimizar o processamento de dados é uma das principais preocupações do time de engenharia da Afya. Recentemente, buscamos aprimorar nossos conhecimentos sobre Data Warehouse, contando com a colaboração da equipe da AWS.

O Data Warehouse é um sistema que atua como um repositório centralizado para gerenciar, armazenar e analisar diferentes fontes de dados. Em nossa empresa, utilizamos a ferramenta Redshift, disponibilizada pela AWS, que nos permite acessar, manipular e processar grandes volumes de dados com eficiência. Com tabelas que chegam a 20 milhões de registros por dia, a escalabilidade do Redshift se mostra fundamental para suportar nossas necessidades de processamento de dados.

Neste post, o objetivo é fornecer uma visão geral sobre o funcionamento do Redshift, não apenas para os engenheiros de dados que atuam diretamente com a ferramenta, mas também para aqueles que ainda não a conhecem e desejam compreender melhor como ela funciona nos bastidores.

>> Propriedades e Organização

O Redshift é conhecido por utilizar o conceito ACID em suas transações, o que garante que elas possuam quatro propriedades fundamentais: atomicidade, consistência, isolamento e durabilidade. Esse recurso é essencial para assegurar que as consultas sejam executadas de forma segura e eficiente, evitando conflitos com outras transações. Além disso, as informações são preservadas mesmo em caso de falhas do sistema e as transações são executadas apenas após o ciclo completo, garantindo que nenhum processo seja entregue parcialmente.

Ao criar uma estrutura de dados no Redshift, ele cria por padrão uma única database, que contém a organização dos modelos em um esquema hierárquico: Database > Schema > Table/view.

O Redshift segue o modelo de schema-on-write, onde é necessário especificar a estrutura, colunas, tipos de dados, entre outros, ao definir um novo modelo em nossos schemas, que agrupam as tabelas. Para desenvolver e criar esses modelos no ambiente do Redshift, é preciso utilizar a linguagem SQL, abaixo temos um exemplo de como criar uma tabela na ferramenta:

CREATE TABLE campaigns.campaigns_submit_leads
(
csl_root_id CHAR(36) ENCODE zstd
,csl_root_tstamp TIMESTAMP WITHOUT TIME ZONE ENCODE RAW
,csl_persona VARCHAR(33) ENCODE zstd
,csl_title VARCHAR(128) ENCODE zstd
,csl_page_bounce INTEGER ENCODE az64
,csl_br_features_pdf BOOLEAN ENCODE zstd
)

DISTSTYLE KEY
DISTKEY (csl_root_id)
SORTKEY (csl_root_tstamp)

No próximo tópico, vamos explorar a arquitetura da ferramenta para compreender o processamento e armazenamento de dados no Redshift. A partir disso, abordaremos como criar uma consulta para a criação de tabelas, utilizando como exemplo a query já mencionada. Vamos analisar cada aspecto da query — como tipo de dados, codificação, chave de distribuição e chave de ordenação — e entender como cada um deles contribui para a performance da tabela.

>> Arquitetura: Processamento e Armazenamento

A arquitetura do Redshift é composta por diversas camadas. Quando os usuários acessam essa ferramenta, a conexão pode ser estabelecida via JDBC (Java Database Connectivity) ou ODBC (Open Database Connectivity). Ambas são APIs utilizadas para conectar um aplicativo a um banco de dados. No entanto, a ODBC é mais adequada para aplicativos que precisam se conectar a diferentes bancos de dados usando linguagens diferentes, enquanto o JDBC é mais indicado para aplicativos desenvolvidos em Java. Na Afya costumamos usar a ferramenta DBeaver para acessar e desenvolver querys no ambiente do Reshift.

Já dentro do Cluster do Redshift, a camada seguinte é o Leader Node. Ele é responsável por receber a query, compilar e definir um plano de processamento para a próxima camada: os Compute Nodes. O Redshift utiliza processamento paralelo, ou seja, o plano de processamento é dividido entre as máquinas disponíveis, e no final do processo, é retornado para a camada anterior.

A quantidade de Compute Nodes é adaptável e pode ser ajustada de acordo com as necessidades da empresa. Quanto mais nodes, mais paralelismo, slices, memória e performance, mas também maior será o custo da ferramenta.

Cada Compute Node é composto por um grupo de Slices, cuja quantidade é definida de acordo com o tamanho e o tipo de node escolhido. Na Afya, trabalhamos com a nova instância Ra3, que nos permite um armazenamento gerenciável e com maior flexibilidade para dimensionar nosso cluster.

O Slice é uma porção de disco e processamento. O disco é onde os dados são armazenados e de acordo com o modo de distribuição escolhido, os dados podem estar em Slices ou em nodes diferentes. Dentro desse disco, os dados são armazenados em blocos de 1MB e diferentemente de alguns bancos transacionais, como o MySQL, que armazena os dados por linha, no Redshift, esse armazenamento é colunar. Isso se deve ao fato de que em um banco analítico, serão realizados vários filtros e joins em queries para análise e, com esse modelo de armazenamento, conseguimos ganhar em performance.

>> Escolha do tipo de dado — Dados estruturados

Quando se trata de armazenamento e processamento de dados em um banco de dados, a escolha dos tipos de dados adequados é fundamental para garantir uma boa performance e precisão na análise dos dados. Existem diversos tipos de dados disponíveis no Redshift, cada um com um conjunto fixo de propriedades associadas. Alguns exemplos de tipos de dados e sua definição de acordo com a AWS:

  • TIMESTAMP WITHOUT TIME ZONE — Data e hora (sem fuso horário)
  • CHAR — String de caracteres com comprimento fixo
  • VARCHAR — String de caracteres de comprimento variável com limite definido pelo usuário
  • INTEGER — Número inteiro de quatro bytes assinado
  • BOOLEAN — Booleanos lógicos (verdadeiro/falso)

Ao utilizar tipos de dados com tamanho maior do que o necessário, como criar uma coluna de dia da semana com VARCHAR(150) em vez de VARCHAR(12), o Redshift reservará em memória um espaço maior do que o necessário.

Quando o sistema processar essa informação, ele tentará processá-la na memória para acelerar o processo, mas se houver muito desperdício dela, parte do processamento precisará ser feito no disco, o que pode afetar negativamente o desempenho. Por isso, é importante evitar o desperdício de memória e escolher os tipos de dados mais adequados para cada coluna.

A lista completa dos tipos de dados pode ser encontrada neste link: AWSTipos de dados

>> Compressão de colunas

Como já mencionado, o Redshift utiliza armazenamento colunar, tornando a compressão das colunas extremamente importante. Por exemplo, ao armazenar a coluna “dia da semana”, todos os dados dessa coluna possuem o mesmo tipo, o que possibilita uma compressão mais eficiente quando aplicada na coluna inteira. O Redshift é capaz de alcançar uma taxa de compressão de 3 a 4 vezes maior que o arquivo original.

Além de reduzir as requisições para armazenamento, a compressão também melhora o desempenho do Redshift, uma vez que a performance deste banco de dados está diretamente ligada ao I/O (Input/Output), ou seja, ligado à comunicação entre dispositivos, envolvendo a transferência de dados entre dispositivo e memória em uma máquina ou processador.

Existem diversos tipos de compressão, podendo ser definida como ENCODE AUTO, onde o próprio Redshift define o tipo ideal, ou pode ser definida manualmente na criação da tabela. Por padrão, a coluna definida como sortkey se mantém sem encode, ou seja, encode RAW. No caso das outras colunas, na Afya costumamos trabalhar com 2 tipos de compressão: o AZ64 e o ZSTD. O AZ64 é utilizado para colunas dos tipos SMALLINT, INTEGER, BIGINT, DECIMAL, DATE, TIMESTAMP e TIMESTAMPTZ, enquanto os demais tipos utilizam o ZSTD.

A lista completa dos tipos de codificação pode ser encontrada neste link: AWS — Codificações de compactação

>> Estilo de distribuição — DISTKEY

Como mencionado anteriormente, quando se cria um plano de processamento de consulta no Redshift, é empregado o paralelismo para a organização dos dados com base nos nodes e slices disponíveis.

Uma propriedade importante para a organização dos dados é o estilo de distribuição da tabela, que permite um melhor controle na organização dos dados armazenados internamente. O objetivo é minimizar a transferência de dados durante a consulta, garantir uma distribuição igualitária dos dados para o processamento paralelo e assegurar que cada nó tenha acesso à mesma quantidade de informações.

A AWS disponibiliza quatro tipos de distribuição, descritos a seguir:

  • AUTO — Essa é a opção padrão do Redshift, em que o estilo de distribuição é determinado com base na interação dos usuários com a tabela. Ele pode alterar dinamicamente o estilo de acordo com o uso atual, mas somente durante períodos de baixa utilização para evitar impactos na performance.
    Por padrão, para tabelas pequenas, a configuração inicial selecionada é ALL. À medida que a tabela cresce, o Redshift pode mudar para o tipo KEY se identificar uma chave adequada de acordo com o padrão de queries sendo processadas. Caso não encontre uma chave ideal, ele pode alterar para o tipo EVEN.
  • KEY—Nesse estilo de distribuição, é necessário definir a coluna-chave para organizar os nodes do cluster, pois dados com o mesmo valor nessa coluna serão armazenados no mesmo slice. É importante escolher uma chave com alta cardinalidade para evitar desequilíbrios de linhas entre os slices. Geralmente, é recomendável escolher uma coluna popular em processos de JOIN e WHERE na criação de queries.
  • ALL — Nesse estilo de distribuição, a tabela é inteira armazenada no primeiro slice de cada node. Quando a tabela é invocada em uma query com diferentes fontes, ela pode ser processada no mesmo node da outra fonte, evitando a transferência de dados entre nós.
    Com isso, embora o tipo all tenha um custo maior de armazenamento, ele proporciona ganhos de desempenho, porém esse tipo de distribuição é recomendado apenas para tabelas estáticas com menos de 3 milhões de linhas.
  • EVEN — Nesse estilo de distribuição, os dados são distribuídos de maneira uniforme em todos os slices disponíveis. Caso sua tabela não se adeque aos estilos de distribuição anteriores, o estilo even é recomendado.

>> Chave de ordenação — SORTKEY

As chaves de ordenação (Sort Keys) têm a função de ordenar fisicamente os dados no disco, e as colunas de data são um excelente exemplo de coluna para essa finalidade. Dentro dos blocos de dados, é possível ordenar e agrupar de acordo com a chave de ordenação escolhida, permitindo que os dados ordenados sejam explorados da melhor forma possível no processamento da query.

Uma opção disponível é a chave AUTO, em que o Redshift monitora o padrão de query e escolhe automaticamente a chave de ordenação mais adequada, normalmente quando a maioria dos filtros ou joins realizados com a tabela são feitos com colunas específicas.

É possível utilizar mais de uma chave de ordenação na mesma tabela, o que é chamado de chave de ordenação composta (compound sort key). É importante definir as colunas de ordenação na ordem crescente de cardinalidade, sem exceder o limite de 4 colunas. A escolha dessas colunas deve ser feita com cuidado, pois dependendo do nível de dificuldade pode gerar problemas durante a ordenação via comando Vacuum.

O VACUUM é um comando que ajuda a classificar as linhas e recuperar espaço na tabela escolhida. O Redshift executa automaticamente o comando nos bastidores durante períodos de baixo uso, mas também é recomendado usá-lo sempre após uma carga significativa ou quando os dados estão com uma % alta de desorganização. O comando que normalmente usamos:

VACUUM FULL campaigns.campaigns_submit_leads TO 100 PERCENT

Uma dica útil é ordenar os dados com o comando “ORDER BY” durante a inserção significativa de dados em uma tabela. Isso pode ajudar na ordenação e tornar o processo de Vacuum posterior menos demorado.

>> Exemplo prático

Após analisarmos o funcionamento e importância de cada tópico previamente descrito, é importante entendermos como esses conceitos se aplicam em um caso real. Para exemplificar, utilizaremos a query mencionada no primeiro tópico e detalharemos seu funcionamento.

CREATE TABLE campaigns.campaigns_submit_leads
(
csl_root_id CHAR(36) ENCODE zstd
,csl_root_tstamp TIMESTAMP WITHOUT TIME ZONE ENCODE RAW
,csl_persona VARCHAR(33) ENCODE zstd
,csl_title VARCHAR(128) ENCODE zstd
,csl_page_bounce INTEGER ENCODE az64
,csl_br_features_pdf BOOLEAN ENCODE zstd
)
DISTSTYLE KEY
DISTKEY (csl_root_id)
SORTKEY (csl_root_tstamp)
  • Tipo de dado: Após uma análise cuidadosa, escolhemos o tipo ideal de dado para cada coluna, considerando a natureza dos dados armazenados em cada uma delas. Como mencionado anteriormente, a escolha do tipo de dado adequado é essencial para garantir um armazenamento eficiente, economizando espaço em disco e aprimorando o desempenho das consultas.
    Tomando como exemplo a coluna csl_root_id, que contém dados de identificadores universais únicos, foi definido um comprimento fixo de 36 caracteres, de acordo com as especificações do time de produto.
    Já no caso da coluna csl_persona, mesmo com dados de tamanho variável, conseguimos manter um comprimento aproximado da capacidade real necessária para o armazenamento adequado.
  • Encode: Na query, cada coluna é codificada com um método de compressão específico, com o objetivo de reduzir o tamanho dos dados armazenados em disco e aprimorar a eficiência das consultas, sem comprometer a precisão das informações. Buscamos seguir os padrões estabelecidos pela AWS para a escolha dos diferentes tipos de codificação.
    Em relação à nossa sortkey, a coluna csl_root_tstamp é codificada como RAW, o que significa que a coluna é armazenada sem compressão para permitir a ordenação eficiente dos dados. Já para a coluna csl_page_bounce, optamos pela codificação AZ64, que é a recomendação da AWS para esse tipo de dado (INTEGER).
    Para as demais colunas, que não se encaixavam no perfil da AZ64, optamos pela codificação ZSTD. Entendemos que essa é uma das técnicas de compressão mais eficientes disponíveis no Redshift atualmente.
  • Estilo de distribuição: Na Afya, trabalhamos com tabelas muito grandes, com bilhões de linhas, o que torna o método ALL inadequado na maioria dos casos. Ao analisar as modelagens que usam a tabela campaigns.campaigns_submit_leads, percebemos que ela é frequentemente usada como fonte de informações adicionais para tabelas de eventos mais completas.
    Essa tabela geralmente é vinculada via JOIN às tabelas de evento por meio do identificador único, o Id. Com isso, para melhorar a eficiência das consultas e utilizar melhor os recursos, optamos por usar a coluna csl_root_id como chave de distribuição nesse caso.
  • Chave de Ordenação: Nosso objetivo é fornecer à equipe de análise períodos de dados cada vez mais abrangentes. No entanto, dada a grande quantidade de dados anuais e a necessidade de acompanhamentos regulares, tornou-se fundamental a organização do ambiente para facilitar tanto nas análises pontuais quanto nas inserções diárias feitas pela nossa ferramenta de transformação de dados, o DBT.
    Um exemplo disso é a escolha da coluna csl_root_tstamp como base para a ordenação. Por ser a coluna principal de data na tabela, ela ajuda no processo de modelagem incremental. Isso ocorre porque os dados antigos já estão incluídos na tabela final, que contém a carga histórica.
    Portanto, ao inserir dados mais recentes, não é necessário realizar uma análise completa da tabela. Basta atualizar os dados com as datas mais recentes.

Compreender cada vez mais o funcionamento do Redshift tem sido fundamental para planejar melhorias em nosso Data Warehouse. Ao seguir as especificações citadas acima, conseguimos reduzir significativamente o tempo de processamento. Quando lidamos com um alto volume de dados — como é o caso da Afya, em que chegamos a tabelas com bilhões de linhas — dedicar esforços à melhoria de desempenho se torna vital para o bom funcionamento da ferramenta.

Nosso time multidisciplinar, formado por engenheiros, analistas e cientistas, tem como objetivos aprimorar e aproveitar ao máximo as funcionalidades da ferramenta, além de tornar o ambiente de utilização mais acessível e construtivo para outros times que a utilizam ativamente. Se você tiver alguma sugestão, dúvida ou comentário sobre o conteúdo, sinta-se à vontade para entrar em contato comigo pelo Linkedin.

English version: https://medium.com/@alice_thomaz/1dfcd6512746

--

--