Pular para o conteúdo principal

Performance e escalabilidade sem limites: domine o design de banco de dados de tabela única com DynamoDB

Uma tabela para mandar em todas: simplifique, escale e turbine seu banco NoSQL!
Atualizado 17 de set. de 2026  · 12 min lido

Explorar com IA

ChatGPTClaudePerplexity

Bem-vindo ao mundo do DynamoDB, onde eficiência e escalabilidade praticamente ilimitada se encontram em uma única tabela. Neste guia completo, vamos embarcar em uma jornada para revelar todo o potencial do design de banco NoSQL com tabela única.

Descubra como simplificar modelos de dados complexos enquanto aproveita toda a força de uma estrutura unificada, pronta para acompanhar suas necessidades em constante crescimento.

Seja você um desenvolvedor experiente em bancos NoSQL ou esteja apenas começando nessa área, prepare-se para levar a escalabilidade dos seus aplicativos a um novo patamar com uma arquitetura de dados poderosa!

O que é NoSQL?

Antes de começar, é importante entender a diferença entre NoSQL e banco relacional, em quais cenários usar cada um e por que essas tecnologias foram criadas. Para isso, vale um breve olhar na história do processamento de dados.

Você pode ler mais sobre a importância dos bancos NoSQL na ciência de dados em um post separado.

Bancos relacionais

O banco relacional é uma tecnologia conhecida e amplamente utilizada desde a década de 1970. Dois dos principais problemas que ele resolve são armazenamento e integridade referencial.

Os dados são “normalizados”, geralmente até a terceira forma normal, o que reduz a pegada de armazenamento e garante que qualquer atualização permaneça consistente com o restante dos dados.

O tamanho da pegada de dados persistidos era crucial porque, historicamente, o componente mais caro no data center era o disco rígido. Hoje não é mais assim: o recurso mais caro é a CPU, e o custo de armazenamento caiu drasticamente. Isso não torna o banco relacional obsoleto; ele segue sendo adequado para:

  • OLAP
  • Data warehouses
  • Consultas ad hoc
  • Aplicações com padrões de acesso pouco definidos
  • CMS / headless CMS

Bancos NoSQL

“Pressão de dados” é a capacidade de um sistema processar um determinado volume de dados com custo e/ou tempo razoáveis.

Os bancos NoSQL surgiram no fim dos anos 1990 para lidar com a pressão de dados que afetava o desempenho dos bancos relacionais diante do crescimento constante do volume de dados das aplicações.

A CPU em um banco relacional costuma ser um gargalo, já que o servidor precisa buscar e juntar dados normalizados para formar visões desnormalizadas que os aplicativos consomem.

Isso pode ser aliviado com réplicas de leitura, mas o NoSQL segue outro caminho: armazena os dados já desnormalizados, prontos para consumo pela aplicação. A pegada de dados fica maior do que em um banco relacional, porém a carga na CPU diminui, fazendo o servidor atuar como um roteador simples que usa um algoritmo de hash para apontar para o local no disco no data center. Usos típicos de bancos NoSQL incluem:

  • OLTP
  • Aplicações com padrões de acesso bem definidos
  • Aplicações que precisam escalar horizontalmente para volumes regionais ou globais

Diversos estudos já nos deram dados objetivos suficientes para representar, em um gráfico, o desempenho de SQL vs. NoSQL em grande escala.

gráfico de comparação de operações em massa

Fonte da imagem

Um último ponto: os dados armazenados em um banco NoSQL ainda são dados relacionais. Se não fossem relacionais, guardaríamos em um simples repositório de arquivos.

Antipadrões em NoSQL

Não modelamos um banco NoSQL da mesma forma que um relacional, então é antipadrão traduzir um design normalizado diretamente para NoSQL usando várias tabelas com junções, porque NoSQL não tem operador de join nem integridade referencial.

Uma tabela em NoSQL é equivalente a um catálogo em um banco relacional. Em vez disso, modele os dados para ficarem desnormalizados, prontos para o consumo pela aplicação.

Outro antipadrão são as Hot Keys. Isso significa que a maior parte (ou todas) as requisições atingem um único nó de armazenamento — sinal de que o espaço de chaves não foi bem projetado.

O mapa de calor de acesso ficaria mais ou menos assim:

image14.png

DynamoDB

O AWS DynamoDB é um serviço totalmente gerenciado, serverless, do tipo wide column key-value, o que significa que ele suporta muitos itens (linhas) em uma tabela, e esses itens não precisam ter os mesmos atributos. Os tipos de dados compatíveis são:

  • Tipos escalares: string, number, binary, boolean, null
  • Tipos de documento: estruturas complexas com atributos aninhados (ex.: JSON)
  • Tipos de conjunto: string set, number set, binary set

Como já mencionado, NoSQL deve ser usado quando os padrões de acesso da aplicação são bem conhecidos e há necessidade de suportar um grande número de TPS. O DynamoDB pode escalar para qualquer carga e mantém tempos de resposta previsíveis e consistentes de até 4 milhões de TPS com baixa latência (10–20 ms).

Como o DynamoDB funciona?

Cada item (ou linha) tem uma partition key que o identifica de forma única e determina a distribuição dos dados no armazenamento subjacente.

Itens podem ter, opcionalmente, uma sort key, que define a ordem em que os dados são gravados em disco dentro de uma partição. As sort keys permitem consultar partições com operações de faixa e filtros mais complexos (embora os filtros sejam aplicados na camada do cliente, não no banco).

A partition key é usada para criar um índice de hash, e cada item é distribuído conforme esse hash em um espaço de chaves virtual. Esse espaço é dividido em segmentos que mapeiam para dispositivos de armazenamento físicos, podendo crescer e encolher dinamicamente conforme o volume de dados.

É por isso que o DynamoDB é rápido e consistente em qualquer escala: o mecanismo do banco atua como um serviço de roteamento de chaves “hasheadas” até o armazenamento físico.

Modelagem de dados com NoSQL

Neste tutorial, vamos modelar uma loja online chamada Daintree.com.

(Curiosidade: a floresta Daintree, em Queensland, Austrália, é uma das mais antigas do planeta — estima-se que tenha cerca de 180 milhões de anos!)

Primeiro, vamos criar um diagrama de relacionamento de entidades (ERD) bem simplificado para visualizar o que vamos modelar.

diagrama de relacionamento de entidades (ERD)

A partir disso, deduzimos que um cliente pode criar um carrinho e adicionar vários produtos com quantidades diferentes.

Um produto tem uma categoria, que por sua vez pode ter categorias-pai.

Carrinhos podem virar pedidos, que podem ser divididos em vários pagamentos.

Como já dito, essa é uma visão altamente simplificada dos relacionamentos e atributos das entidades que compõem um aplicativo de compras online, mas serve ao propósito deste tutorial.

Defina os padrões de acesso

Em um banco relacional, geralmente não nos preocupamos tanto com esta etapa. SQL é uma linguagem extremamente poderosa que, desde que o esquema esteja bem desenhado, nos dá flexibilidade para consultar e manipular dados como quisermos. Em NoSQL com design de tabela única não é assim: precisamos armazenar os dados desnormalizados e prontos para consumo direto pela aplicação.

Veja alguns padrões de acesso que podemos querer suportar na nossa aplicação de e-commerce:

  • Consultar todos os produtos por categoria
  • Consultar as subcategorias de uma categoria-pai
  • Consultar todos os pedidos por cliente
  • Consultar todos os pagamentos de um pedido
  • Consultar um pedido específico de um cliente
  • Consultar todos os itens do carrinho de um cliente
  • Consultar todos os produtos pelo histórico de pedidos do cliente

Modelando a tabela

O NoSQL Workbench é uma ferramenta gratuita da AWS para modelar tabelas do DynamoDB.

Vá em Data modeler e crie um novo modelo com uma nova tabela:

image5.png

Vamos manter os atributos da chave primária como strings genéricas PK e SK:

image8.png

Para atributos adicionais, vamos armazenar o entity_type e dois pares de atributos de chave primária para GSI (PK e SK).

Um GSI é um global secondary index, que é uma cópia da sua tabela que o DynamoDB mantém sincronizada (de forma assíncrona!!) mas armazenada usando pares alternativos de chaves primárias. Esse é o mecanismo-chave de como uma única tabela suporta múltiplos padrões de acesso. Mais adiante falaremos disso; por ora, entenda que essas chaves guardarão as chaves primárias de entidades relacionadas.

Os outros atributos adicionais que precisamos adicionar são todos os atributos não-chave dos nossos dados (observe que esta etapa é apenas para a fase de design e não será explicitamente definida quando provisionarmos a tabela real):

image11.png

Em seguida, adicionamos os dois GSIs usando as chaves que definimos na etapa anterior:

image9.png

Clique em salvar e depois em visualize the data model. Você verá a tabela recém-criada, ainda vazia. Clique em edit table e, no canto superior direito, em edit data; isso permitirá adicionar novas linhas:

image7.png

Prefixos de dados

Antes de começar a inserir dados, precisamos definir os prefixos de chave primária para cada entidade:

product

p#

category

c#

customer (user)

u#

basket

b#

basket item

bi#

order

o#

order line

ol#

payment

py#

invoice

i#

Modelando os dados

Dados do cliente

A maioria dos padrões de acesso gira em torno do cliente, então no índice primário usaremos o ID do cliente como partição principal, e o próprio registro do cliente será identificado com a sort key também sendo o ID do cliente.

Assim, podemos relacionar facilmente clientes a pedidos e clientes a carrinhos reutilizando a mesma partition key, mas com uma sort key exclusiva.

Nossa visão agregada fica assim:

image12.png

Agora podemos consultar os três registros em um único comando, buscando toda a partição desse cliente:

export const getAllCustomerRecords = async (id: string) =>
  dynamoClient
    .query({
      TableName: TABLE_NAME,
      KeyConditionExpression: "pk=:pk",
      ExpressionAttributeValues: {
        ":pk": valueToAttributeValue(addPrefix(id, CUSTOMER_PREFIX)),
      },
    })
    .then((result) => result.Items);

À medida que a tabela cresce, esse tipo de consulta pode não ser a mais eficiente, pois retornará todo e qualquer pedido do cliente, além de outros registros como carrinhos e notas fiscais. Mas é bom saber que conseguimos acessar todos os dados do cliente em uma única query quando necessário.

Com um pequeno ajuste em ExpressionAttributeValues para incluir uma correspondência parcial (begins_with) na sort key, conseguimos consultar:

  • Todos os registros de carrinho do cliente
  • Todos os registros de pedidos do cliente

O mais importante aqui é entender como modelar relacionamentos um-para-um e um-para-muitos em NoSQL.

Categorias

Essas entidades representam outro relacionamento um-para-muitos, em que uma categoria pode conter muitos produtos.

Categorias também têm hierarquia: uma categoria pode ter várias subcategorias, e os produtos podem existir em qualquer nível dessa árvore.

Vamos modelar isso com livros, dividindo em ficção e não ficção.

image18.png

Perceba como preenchemos as chaves de atributos do GSI para as subcategorias, definindo GSI1_PK como o ID da categoria-pai e GSI1_SK como o ID da subcategoria. Isso nos permite consultar todas as subcategorias por categoria usando o GSI1:

image19.png

O mesmo índice, GSI1, pode ser usado não apenas para mapear relacionamentos um-para-muitos. Suponha que queiramos buscar o cliente pelo e-mail; o modelo atual não suporta isso diretamente, a menos que façamos um pequeno ajuste na forma de salvar os dados:

image1.png

Ao armazenar o e-mail do cliente e o tipo de entidade como PK e SK do GSI, nós:

  • Garantimos que o e-mail do cliente seja único (somente para esse GSI; impor exclusividade no índice primário exigiria outra solução)
  • Permitimos que clientes também apareçam como sellers, por exemplo (se tivéssemos esse tipo de entidade)
  • Passamos a buscar um cliente por e-mail em vez de por ID

Para ajudar na distribuição de dados do DynamoDB, poderíamos ir além e armazenar o e-mail como um hash.

export const getCustomerByEmail = async (email: string) =>
  dynamoClient
    .query({
      TableName: TABLE_NAME,
      IndexName: "gsi1",
      KeyConditionExpression: "gsi1_pk = :email AND gsi1_sk = :entityType",
      ExpressionAttributeValues: {
        ":email": valueToAttributeValue(email),
        ":entityType": valueToAttributeValue(entityType),
      },
    })
    .then(({ Items }) => Items?.[0]);

Até aqui, talvez você tenha se perguntado por que todos os índices e atributos de chave têm nomes genéricos, como PK e SK. A resposta agora deve estar clara: os dados que armazenamos e os índices que mantemos são flexíveis e capazes de suportar múltiplos padrões de acesso, dependendo do tipo de entidade.

O Terraform da tabela que usamos até agora é o seguinte:

resource "aws_dynamodb_table" "tutorial-1" {
  name = "tutorial-1"
  billing_mode = "PAY_PER_REQUEST"
  hash_key = "pk"
  range_key = "sk"


  attribute {
    name = "pk"
    type = "S"
  }


  attribute {
    name = "sk"
    type = "S"
  }


  attribute {
    name = "gsi1_pk"
    type = "S"
  }


  attribute {
    name = "gsi1_sk"
    type = "S"
  }


  attribute {
    name = "gsi2_pk"
    type = "S"
  }


  attribute {
    name = "gsi2_sk"
    type = "S"
  }


  global_secondary_index {
    name = "gsi1"
    hash_key = "gsi1_pk"
    range_key = "gsi1_sk"
    projection_type = "ALL"
  }


  global_secondary_index {
    name = "gsi2"
    hash_key = "gsi2_pk"
    range_key = "gsi2_sk"
    projection_type = "ALL"
  }
}

Produtos

Armazenar produtos segue o mesmo padrão um-para-muitos: a partition key é o ID da categoria e a sort key é o ID do produto:

image3.png

Adicionar a categoria-pai ao GSI1 nos permite consultar todos os livros do nosso banco:

image17.png

Em produção, podemos usar DynamoDB Streams para enviar dados a um cluster Elasticsearch e, assim, buscar por título do livro. Mas como usar nosso modelo atual para consultar por título do produto?

Já usamos o GSI1, então poderíamos usar o GSI2 para isso:

image2.png

Lembre-se: ao consultar o DynamoDB, você precisa fornecer a partition key completa e exata. Portanto, embora essa implementação possa não ser a mais otimizada, ela ilustra como três índices podem atender a diferentes padrões de acesso.

No meu próximo tutorial, vou mostrar como resolver isso integrando a um índice de busca.

Conteúdo do carrinho e linhas do pedido

Tanto os itens do carrinho quanto as linhas do pedido ficam na partição do cliente; podemos usar os GSIs para consultar todos os produtos em um carrinho, todos os produtos de um pedido e todos os produtos que um cliente já comprou.

image13.png

image4.png

image16.png

Este último exemplo mostra o histórico de pedidos de um cliente com dois produtos feitos em dois pedidos diferentes.

Para quem se interessa por modelagem de dados em bancos SQL, nosso webinar Data Modeling in SQL traz ótimos insights que complementam o entendimento de modelagem NoSQL.

Desafios do design com tabela única

Usar uma tabela única com DynamoDB traz muitas vantagens, mas também alguns desafios e pontos de atenção:

Complexidade e lógica da aplicação

Concentrar todos os dados em uma única tabela pode se tornar complexo, especialmente à medida que sua aplicação cresce e evolui.

Você precisa planejar e estruturar os dados com cuidado para acomodar diversos padrões de consulta. Além disso, a eficácia do design de tabela única está intimamente ligada à lógica da aplicação, que é crucial para garantir acesso fluido e consultas eficientes.

Pensar bem tanto na estrutura dos dados quanto na lógica da aplicação é essencial para esse design funcionar.

Sobrecarga de índices

Embora o DynamoDB permita Global Secondary Indexes para suportar diferentes padrões de consulta, é preciso provisioná-los e mantê-los, o que pode aumentar custos e complexidade.

Escolha da chave de partição

Escolher a partition key correta é fundamental para distribuição uniforme dos dados e bom desempenho. Uma escolha ruim pode gerar partições quentes e gargalos.

Tamanho e custo

Com o aumento do volume, a sua tabela única pode crescer bastante. Isso afeta tanto o custo de armazenamento quanto o custo de leitura e escrita.

Sem busca full-text

O DynamoDB não oferece busca full-text nativa, então implementar isso exige integrar com serviços como Elasticsearch.

Mudanças de esquema

Alterar o esquema da tabela pode ser desafiador, especialmente em produção. Você pode precisar migrar dados existentes ou adaptar a lógica da aplicação.

Considerações de consistência

Dependendo do tipo de leitura (forte ou eventual), pode haver desafios de consistência, lembrando que GSIs são replicados de forma assíncrona.

Flexibilidade limitada de consulta

As consultas do DynamoDB são poderosas, mas não tão flexíveis quanto SQL.

Para entender melhor como projetar bancos de dados e navegar por esses desafios, confira nosso curso de Database Design.

Conclusão

Ao trabalhar com um banco NoSQL, é fundamental se desprender dos padrões familiares de design relacional — é preciso “desaprender” alguns hábitos.

Optar pelo design de tabela única oferece a promessa de um banco escalável globalmente, pronto para lidar com alto tráfego com desempenho previsível e confiável.

Mas essa vantagem não vem de graça: exige planejamento minucioso e uma implementação cuidadosa da estrutura do banco e da lógica da aplicação desde o início.

Aprofunde seu conhecimento com nosso curso de NoSQL concepts!


Gary Alway's photo
Author
Gary Alway
LinkedIn

Sou engenheiro de software de pilha completa e arquiteto de soluções, apaixonado por aprendizado e dados!

Tópicos
Data Analysis

Comece sua jornada em dados hoje mesmo!

Curso

Conceitos de NoSQL

2 h
19K
Neste curso conceitual (sem código), conheça os quatro principais tipos de bancos NoSQL e plataformas populares.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

blog

Contratos de dados desmistificados: Tudo o que você precisa saber

Obtendo escalabilidade em sistemas de dados distribuídos e reduzindo erros.
Mike Shakhomirov's photo

Mike Shakhomirov

11 min

blog

Uma introdução ao DuckDB: O que é e por que você deve usá-lo?

Explore o DuckDB, o banco de dados analítico rápido e fácil de usar para Python e R. Conheça seus principais recursos, casos de uso e como ele otimiza as tarefas de análise de dados.
Kurtis Pykes 's photo

Kurtis Pykes

7 min

blog

Bancos de dados NoSQL: O que todo cientista de dados precisa saber

Descubra para que servem os bancos de dados NoSQL, por que os cientistas de dados os utilizam e uma lista dos melhores bancos de dados NoSQL disponíveis.
Zoumana Keita 's photo

Zoumana Keita

12 min

Tutorial

SELEÇÃO de várias colunas no SQL

Saiba como selecionar facilmente várias colunas de uma tabela de banco de dados em SQL ou selecionar todas as colunas de uma tabela em uma consulta simples.
DataCamp Team's photo

DataCamp Team

3 min

Tutorial

Criando e personalizando tabelas dinâmicas no Power BI

Saiba como criar tabelas dinâmicas personalizáveis no Power BI com formatação condicional avançada e algumas dicas de otimização.
Joleen Bothma's photo

Joleen Bothma

9 min

Tutorial

Primeiros passos com o AWS Athena: Um guia prático para iniciantes

Este guia prático ajudará você a começar a usar o AWS Athena. Explore sua arquitetura e seus recursos e saiba como consultar dados no Amazon S3 usando SQL.
Tim Lu's photo

Tim Lu

15 min

Ver MaisVer Mais