Cloud & Infraestrutura

Como Usar DynamoDB: Data Modeling, GSIs, LSIs e Capacity Modes em Produção

8 min de leitura

Como Usar DynamoDB: Data Modeling, GSIs, LSIs e Capacity Modes em Produção

Data Modeling no DynamoDB O DynamoDB é um banco de dados NoSQL totalmente gerenciado da AWS que exige uma abordagem de modelagem fundamentalmente diferente de bancos relacionais. A chave está em pensar em como você vai consultar os dados, não apenas em como armazená-los. Diferentemente do SQL, você define uma chave primária (Partition Key + Sort Key opcional) que determina como os dados são distribuídos e recuperados. Uma boa modelagem começa com identificar seus padrões de acesso. Se você precisa buscar usuários por ID e depois por email, não crie duas tabelas — use uma Partition Key ( ) e crie um GSI (Global Secondary Index) para email. Evite o máximo possível queries que varrem toda a tabela. O exemplo abaixo mostra uma tabela de e-commerce onde o padrão de acesso é: buscar pedidos por usuário, e secundariamente por status. Índices Secundários: GSI vs LSI Global Secondary Indexes (GSI) GSIs permitem consultar dados usando uma chave primária diferente, com throughput

<h2>Data Modeling no DynamoDB</h2>

<p>O DynamoDB é um banco de dados NoSQL totalmente gerenciado da AWS que exige uma abordagem de modelagem fundamentalmente diferente de bancos relacionais. A chave está em pensar em <em>como você vai consultar os dados</em>, não apenas em como armazená-los. Diferentemente do SQL, você define uma chave primária (Partition Key + Sort Key opcional) que determina como os dados são distribuídos e recuperados.</p>

<p>Uma boa modelagem começa com identificar seus padrões de acesso. Se você precisa buscar usuários por ID e depois por email, não crie duas tabelas — use uma Partition Key (<code>userId</code>) e crie um GSI (Global Secondary Index) para email. Evite o máximo possível queries que varrem toda a tabela. O exemplo abaixo mostra uma tabela de e-commerce onde o padrão de acesso é: buscar pedidos por usuário, e secundariamente por status.</p>

<pre><code class="language-python">import boto3

from decimal import Decimal

dynamodb = boto3.resource(&#039;dynamodb&#039;)

Criar tabela com modelagem adequada

table = dynamodb.create_table(

TableName=&#039;Orders&#039;,

KeySchema=[

{&#039;AttributeName&#039;: &#039;userId&#039;, &#039;KeyType&#039;: &#039;HASH&#039;}, # Partition Key

{&#039;AttributeName&#039;: &#039;orderId&#039;, &#039;KeyType&#039;: &#039;RANGE&#039;} # Sort Key

],

AttributeDefinitions=[

{&#039;AttributeName&#039;: &#039;userId&#039;, &#039;AttributeType&#039;: &#039;S&#039;},

{&#039;AttributeName&#039;: &#039;orderId&#039;, &#039;AttributeType&#039;: &#039;S&#039;},

{&#039;AttributeName&#039;: &#039;orderStatus&#039;, &#039;AttributeType&#039;: &#039;S&#039;}

],

BillingMode=&#039;PAY_PER_REQUEST&#039;

)

Inserir item com dados desnormalizados (padrão NoSQL)

table.put_item(Item={

&#039;userId&#039;: &#039;user#123&#039;,

&#039;orderId&#039;: &#039;order#456&#039;,

&#039;orderStatus&#039;: &#039;DELIVERED&#039;,

&#039;totalAmount&#039;: Decimal(&#039;199.99&#039;),

&#039;items&#039;: [

{&#039;sku&#039;: &#039;PROD#001&#039;, &#039;quantity&#039;: 2},

{&#039;sku&#039;: &#039;PROD#002&#039;, &#039;quantity&#039;: 1}

],

&#039;createdAt&#039;: &#039;2024-01-15T10:30:00Z&#039;

})</code></pre>

<h2>Índices Secundários: GSI vs LSI</h2>

<h3>Global Secondary Indexes (GSI)</h3>

<p>GSIs permitem consultar dados usando uma chave primária diferente, com throughput independente. Você pode criar, atualizar e deletar GSIs sem parar a tabela. São úteis quando você precisa de padrões de acesso totalmente novos. Um GSI consome throughput <em>separado</em> da tabela base — você paga por leitura/escrita no GSI.</p>

<pre><code class="language-python"># Adicionar GSI para buscar pedidos por status

table.update_table(

AttributeDefinitions=[

{&#039;AttributeName&#039;: &#039;orderStatus&#039;, &#039;AttributeType&#039;: &#039;S&#039;},

{&#039;AttributeName&#039;: &#039;createdAt&#039;, &#039;AttributeType&#039;: &#039;S&#039;}

],

GlobalSecondaryIndexUpdates=[

{

&#039;Create&#039;: {

&#039;IndexName&#039;: &#039;OrderStatusIndex&#039;,

&#039;KeySchema&#039;: [

{&#039;AttributeName&#039;: &#039;orderStatus&#039;, &#039;KeyType&#039;: &#039;HASH&#039;},

{&#039;AttributeName&#039;: &#039;createdAt&#039;, &#039;KeyType&#039;: &#039;RANGE&#039;}

],

&#039;Projection&#039;: {&#039;ProjectionType&#039;: &#039;ALL&#039;},

&#039;BillingMode&#039;: &#039;PAY_PER_REQUEST&#039;

}

}

]

)

Consultar usando GSI

response = table.query(

IndexName=&#039;OrderStatusIndex&#039;,

KeyConditionExpression=&#039;orderStatus = :status&#039;,

ExpressionAttributeValues={&#039;:status&#039;: &#039;PENDING&#039;}

)</code></pre>

<h3>Local Secondary Indexes (LSI)</h3>

<p>LSIs compartilham throughput com a tabela base e devem ser criados <em>no momento da criação da tabela</em>. São limitados a 10 GB por partition key. Use LSIs quando você quer ordenar resultados de forma diferente mantendo a mesma partition key. Para a maioria dos casos de produção, GSIs são preferíveis.</p>

<pre><code class="language-python"># Criar tabela com LSI (apenas na criação)

table = dynamodb.create_table(

TableName=&#039;UserActivity&#039;,

KeySchema=[

{&#039;AttributeName&#039;: &#039;userId&#039;, &#039;KeyType&#039;: &#039;HASH&#039;},

{&#039;AttributeName&#039;: &#039;timestamp&#039;, &#039;KeyType&#039;: &#039;RANGE&#039;}

],

AttributeDefinitions=[

{&#039;AttributeName&#039;: &#039;userId&#039;, &#039;AttributeType&#039;: &#039;S&#039;},

{&#039;AttributeName&#039;: &#039;timestamp&#039;, &#039;AttributeType&#039;: &#039;N&#039;},

{&#039;AttributeName&#039;: &#039;activityType&#039;, &#039;AttributeType&#039;: &#039;S&#039;}

],

LocalSecondaryIndexes=[

{

&#039;IndexName&#039;: &#039;UserActivityTypeIndex&#039;,

&#039;KeySchema&#039;: [

{&#039;AttributeName&#039;: &#039;userId&#039;, &#039;KeyType&#039;: &#039;HASH&#039;},

{&#039;AttributeName&#039;: &#039;activityType&#039;, &#039;KeyType&#039;: &#039;RANGE&#039;}

],

&#039;Projection&#039;: {&#039;ProjectionType&#039;: &#039;ALL&#039;}

}

],

BillingMode=&#039;PAY_PER_REQUEST&#039;

)</code></pre>

<h2>Capacity Modes em Produção</h2>

<h3>Provisioned vs On-Demand</h3>

<p>O modo <strong>Provisioned</strong> exige que você defina RCUs (Read Capacity Units) e WCUs (Write Capacity Units) antecipadamente. Cada RCU permite uma leitura fortemente consistente de até 4 KB por segundo; cada WCU permite uma escrita de até 1 KB por segundo. Use quando sua carga é previsível e consistente — é mais barato em geral.</p>

<p>O modo <strong>On-Demand</strong> (PAY_PER_REQUEST) cobra por requisição: aproximadamente $0.25 por milhão de RCUs e $1.25 por milhão de WCUs. Ideal para cargas impredizíveis, desenvolvimento e spikes de tráfego. Não há limite hard — a AWS escala automaticamente.</p>

<pre><code class="language-python"># Exemplo: Provisioned Mode para aplicação com carga conhecida

table = dynamodb.create_table(

TableName=&#039;ProductCatalog&#039;,

KeySchema=[{&#039;AttributeName&#039;: &#039;productId&#039;, &#039;KeyType&#039;: &#039;HASH&#039;}],

AttributeDefinitions=[{&#039;AttributeName&#039;: &#039;productId&#039;, &#039;AttributeType&#039;: &#039;S&#039;}],

BillingMode=&#039;PROVISIONED&#039;,

ProvisionedThroughputCapacity={

&#039;ReadCapacityUnits&#039;: 100,

&#039;WriteCapacityUnits&#039;: 50

}

)

Exemplo: On-Demand para novos serviços ou tráfego variável

table = dynamodb.create_table(

TableName=&#039;UserSessions&#039;,

KeySchema=[{&#039;AttributeName&#039;: &#039;sessionId&#039;, &#039;KeyType&#039;: &#039;HASH&#039;}],

AttributeDefinitions=[{&#039;AttributeName&#039;: &#039;sessionId&#039;, &#039;AttributeType&#039;: &#039;S&#039;}],

BillingMode=&#039;PAY_PER_REQUEST&#039;

)</code></pre>

<h3>Prática em Produção</h3>

<p>Para produção, comece com On-Demand se não conhecer a carga. Monitore via CloudWatch — se seu padrão se torna previsível, migre para Provisioned com Auto Scaling. Configure alarmes para throttling (quando você esgota sua capacidade) e use o CloudWatch Insights para analisar hot partitions (uma partition key recebendo tráfego desproporcional).</p>

<pre><code class="language-python"># Configurar Auto Scaling para Provisioned Mode

autoscaling = boto3.client(&#039;application-autoscaling&#039;)

autoscaling.register_scalable_target(

ServiceNamespace=&#039;dynamodb&#039;,

ResourceId=&#039;table/Orders&#039;,

ScalableDimension=&#039;dynamodb:table:WriteCapacityUnits&#039;,

MinCapacity=5,

MaxCapacity=1000

)

autoscaling.put_scaling_policy(

PolicyName=&#039;OrdersWriteScaling&#039;,

ServiceNamespace=&#039;dynamodb&#039;,

ResourceId=&#039;table/Orders&#039;,

ScalableDimension=&#039;dynamodb:table:WriteCapacityUnits&#039;,

PolicyType=&#039;TargetTrackingScaling&#039;,

TargetTrackingScalingPolicyConfiguration={

&#039;TargetValue&#039;: 70.0,

&#039;PredefinedMetricSpecification&#039;: {

&#039;PredefinedMetricType&#039;: &#039;DynamoDBWriteCapacityUtilization&#039;

}

}

)</code></pre>

<h2>Otimizações e Boas Práticas</h2>

<p>Sempre use <strong>batch operations</strong> (BatchGetItem, BatchWriteItem) em vez de requisições individuais — reduz latência e custo. Implemente <strong>exponential backoff</strong> para throttling automático. Use <strong>sparse indexes</strong> — só projete atributos que você realmente consultará no GSI, economizando throughput. Evite scans; eles leem tudo e são caros. Se precisar migrar dados, use o DMS (Data Migration Service) ou Lambda com streams.</p>

<p>Considere o <strong>DynamoDB Streams</strong> para capturar mudanças em tempo real — perfeito para sincronizar com Elasticsearch ou disparar Lambdas. Na modelagem, use <strong>partition keys com boa distribuição</strong> — não use booleanos ou enums com poucos valores (criarão hot partitions). Para casos raros, use <strong>Query com FilterExpression</strong>, mas lembre que o filtro é aplicado <em>após</em> a leitura, portanto ainda consome RCU.</p>

<h2>Conclusão</h2>

<p>DynamoDB exige <strong>pensar primeiro em padrões de acesso</strong>, não em estrutura de dados. Domine GSIs (criação flexível, throughput independente), use On-Demand em produção inicial e migre para Provisioned com Auto Scaling conforme a carga se estabiliza. Monitore hot partitions e throttling no CloudWatch — a escalabilidade é automática, mas uma boa modelagem e escolha de chaves determinam seu custo e performance final.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html" target="_blank" rel="noopener noreferrer">AWS DynamoDB Developer Guide - Data Modeling</a></li>

<li><a href="https://www.dynamodbguide.com/" target="_blank" rel="noopener noreferrer">DynamoDB Design Patterns - Alex DeBrie</a></li>

<li><a href="https://pages.awscloud.com/rs/112-TZM-766/images/DynamoDB%20Deep%20Dive%20Advanced%20Design%20Patterns%20-%20DAT403.pdf" target="_blank" rel="noopener noreferrer">AWS re:Invent 2019 - DynamoDB Deep Dive</a></li>

<li><a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html" target="_blank" rel="noopener noreferrer">Boto3 DynamoDB Resource Documentation</a></li>

<li><a href="https://aws.amazon.com/dynamodb/pricing/" target="_blank" rel="noopener noreferrer">DynamoDB On-Demand vs Provisioned - AWS Pricing</a></li>

</ul>

Comentários

Mais em Cloud & Infraestrutura

EC2 Auto Scaling: Launch Templates, Policies e Predictive Scaling: Do Básico ao Avançado
EC2 Auto Scaling: Launch Templates, Policies e Predictive Scaling: Do Básico ao Avançado

Launch Templates: Fundação do Auto Scaling Um Launch Template é um blueprint...

O que Todo Dev Deve Saber sobre AWS Organizations: Multi-account Strategy e Control Tower
O que Todo Dev Deve Saber sobre AWS Organizations: Multi-account Strategy e Control Tower

AWS Organizations: Fundamentos e Arquitetura Multi-conta AWS Organizations é...

Dominando EC2 em Profundidade: Instance Types, AMIs e Placement Groups em Projetos Reais
Dominando EC2 em Profundidade: Instance Types, AMIs e Placement Groups em Projetos Reais

Instance Types: Escolhendo a Máquina Correta Os tipos de instância EC2 são or...