NoSQL · Key-Value / Documento

DynamoDB — Estudo
Aprofundado

Do modelo de itens e atributos à arquitetura interna de particionamento, passando por modelagem orientada a access patterns, índices secundários, transações e operação em produção. Cada tópico traz teoria, exemplo prático em AWS CLI ou boto3, um caso de uso do dia a dia e link para a documentação oficial da AWS.

28 tópicos
AWS CLI + SDKs oficiais
Foco: modelagem + arquitetura interna
Recursos gerais
01Crie uma conta AWS e use o Free Tier do DynamoDB (25 GB de armazenamento + capacidade dentro do limite gratuito) para praticar sem custo
02Use o AWS CloudShell (direto do console, sem instalar nada) ou instale o AWS CLI v2 e rode aws configure
03Modele e visualize tabelas antes de criar em produção com o NoSQL Workbench (ferramenta oficial gratuita)
04No DynamoDB a modelagem começa pelos access patterns (as consultas que a aplicação vai fazer) — defina-os antes de desenhar a chave primária
01

Fundamentos

O modelo de dados chave-valor/documento do DynamoDB

Tabelas, itens e atributosBásico
A unidade básica de dados no DynamoDB

Uma tabela é uma coleção de itens, e cada item é uma coleção de atributos (pares nome-valor) — parecido com uma linha numa tabela relacional, mas sem colunas fixas. Além dos atributos que formam a chave primária, cada item pode ter um conjunto totalmente diferente de atributos dos demais itens da mesma tabela.

TeoriaO tamanho máximo de um item é 400 KB, contando nomes de atributos, valores (em UTF-8) e os atributos da chave primária. Esse limite é proposital: o DynamoDB é pensado para acesso item-level de baixa latência, não para guardar blobs grandes — arquivos e mídia devem ficar no S3, com apenas a referência (URL/chave) guardada no item.
Dia a diaUm perfil de usuário de app mobile — nome, e-mail e preferências — vira um único item na tabela Usuarios; um usuário com endereço cadastrado tem um atributo endereco a mais que outro que nunca cadastrou, sem precisar de coluna nula em massa.
AWS CLI
# Criar uma tabela simples (chave primária = apenas partition key)
aws dynamodb create-table \
  --table-name Usuarios \
  --attribute-definitions AttributeName=userId,AttributeType=S \
  --key-schema AttributeName=userId,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST
Chave primária: simples vs. compostaBásico
Partition key vs. partition key + sort key

Uma chave primária simples usa só a partition key — cada valor precisa ser único na tabela. Uma chave primária composta usa partition key + sort key — vários itens podem compartilhar a mesma partition key, desde que o sort key seja diferente entre eles, e ficam ordenados pelo sort key.

TeoriaA escolha da partition key não é só sobre unicidade: ela determina como o DynamoDB distribui fisicamente os dados entre partições internas (fase 2). Já o sort key permite consultas eficientes de "faixa" dentro de uma mesma partition key — como intervalos de data ou prefixos — usando Query em vez de Scan.
Dia a diaUma tabela de pedidos com clienteId como partition key e pedidoId como sort key permite buscar "todos os pedidos do cliente X" com uma única Query eficiente, sem tocar nos pedidos de outros clientes.
AWS CLI
# Criar tabela com chave primária composta
aws dynamodb create-table \
  --table-name Pedidos \
  --attribute-definitions \
      AttributeName=clienteId,AttributeType=S \
      AttributeName=pedidoId,AttributeType=S \
  --key-schema \
      AttributeName=clienteId,KeyType=HASH \
      AttributeName=pedidoId,KeyType=RANGE \
  --billing-mode PAY_PER_REQUEST
Tipos de dados suportadosBásico
Escalares, documentos e conjuntos

O DynamoDB tem três famílias de tipos: escalares (String, Number, Binary, Boolean, Null), documento (List e Map, que permitem aninhamento como um JSON) e conjunto (String Set, Number Set, Binary Set — valores únicos do mesmo tipo escalar).

TeoriaO tipo Number não é um float de ponto flutuante — é armazenado internamente como uma string decimal de até 38 dígitos de precisão, o que evita os erros de arredondamento comuns em ponto flutuante binário. Isso importa em código que serializa itens para JSON puro: a API de baixo nível representa cada valor com um descritor de tipo ({"S": "..."}, {"N": "..."}), enquanto os SDKs de alto nível (como o DynamoDB.DocumentClient ou o resource do boto3) escondem essa representação e trabalham com tipos nativos da linguagem.
Dia a diaUm carrinho de compras guarda os itens como uma List (L) de Maps (M), cada um com produtoId, quantidade e preco — tudo dentro de um único item, sem tabela separada de "itens do carrinho".
JSON — low-level API
{
  "userId": { "S": "u-123" },
  "idade": { "N": "29" },
  "ativo": { "BOOL": true },
  "tags": { "SS": ["vip", "beta"] },
  "carrinho": { "L": [
    { "M": { "produtoId": {"S":"p-1"}, "quantidade": {"N":"2"} } }
  ]}
}
CRUD básicoBásico
PutItem, GetItem, UpdateItem, DeleteItem

As quatro operações item-level são PutItem, GetItem, UpdateItem e DeleteItem — todas identificam o item pela chave primária completa (partition key, ou partition key + sort key).

TeoriaUpdateItem usa update expressions (SET, REMOVE, ADD, DELETE) para modificar atributos específicos in-place, sem reenviar o item inteiro — o que também permite operações atômicas como incrementar um contador (ADD contador :um) sem race condition de leitura-e-escrita.
⚠️PutItem substitui o item inteiro se já existir um com a mesma chave — atributos que não forem enviados somem. Use UpdateItem para alterar campos pontuais, ou uma ConditionExpression em PutItem para evitar sobrescrever sem querer.
AWS CLI
# Create
aws dynamodb put-item --table-name Pedidos --item \
  '{"clienteId":{"S":"c-1"},"pedidoId":{"S":"p-1"},"total":{"N":"149.90"},"status":{"S":"pendente"}}'

# Read
aws dynamodb get-item --table-name Pedidos \
  --key '{"clienteId":{"S":"c-1"},"pedidoId":{"S":"p-1"}}'

# Update — expression, não substituição total
aws dynamodb update-item --table-name Pedidos \
  --key '{"clienteId":{"S":"c-1"},"pedidoId":{"S":"p-1"}}' \
  --update-expression "SET #s = :novo" \
  --expression-attribute-names '{"#s":"status"}' \
  --expression-attribute-values '{":novo":{"S":"enviado"}}'

# Delete
aws dynamodb delete-item --table-name Pedidos \
  --key '{"clienteId":{"S":"c-1"},"pedidoId":{"S":"p-1"}}'
02

Arquitetura Interna

Como o DynamoDB particiona, escala e replica dados

Particionamento e hashingIntermediário
Por que a partition key importa tanto

O DynamoDB distribui automaticamente os dados e o tráfego de uma tabela entre múltiplas partições físicas, usando uma função de hash interna sobre o valor da partition key para decidir em qual partição cada item vive.

TeoriaCada partição tem limites de armazenamento (até 10 GB) e de throughput. Uma partition key de baixa cardinalidade — poucos valores distintos, ou um valor concentrando a maior parte do tráfego — resulta numa hot partition: mesmo que a tabela toda tenha capacidade sobrando, aquela partição específica pode saturar e gerar throttling. Por isso o design da partition key deve buscar alta cardinalidade e distribuição uniforme de acesso, não só unicidade.
⚠️Erros do tipo ProvisionedThroughputExceededException concentrados, mesmo com capacidade agregada sobrando na tabela, são o sintoma clássico de hot partition — o CloudWatch expõe métricas por partição para diagnosticar isso.
Capacity modes: On-Demand vs ProvisionedIntermediário
RCU, WCU e o trade-off de custo/previsibilidade

No modo Provisioned, você define quanta capacidade de leitura (RCU) e escrita (WCU) a tabela terá, opcionalmente com auto scaling. No modo On-Demand, a capacidade escala automaticamente com a carga e a cobrança é por unidade de requisição realmente consumida.

Teoria1 RCU equivale a uma leitura fortemente consistente por segundo de um item de até 4 KB (ou duas leituras eventualmente consistentes do mesmo tamanho); 1 WCU equivale a uma escrita por segundo de um item de até 1 KB. Itens maiores consomem múltiplas unidades proporcionalmente ao tamanho arredondado para cima.
Dia a diaUma startup com tráfego imprevisível (picos de campanha de marketing) usa On-Demand para não precisar prever capacidade; um sistema interno com carga estável e conhecida usa Provisioned + auto scaling, normalmente mais barato nesse cenário.
AWS CLI
# On-Demand
aws dynamodb update-table --table-name Pedidos --billing-mode PAY_PER_REQUEST

# Provisioned, com capacidade explícita
aws dynamodb update-table --table-name Pedidos --billing-mode PROVISIONED \
  --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5
Consistência de leituraIntermediário
Eventualmente consistente vs. fortemente consistente

Por padrão, uma leitura no DynamoDB é eventualmente consistente — pode, raramente, não refletir uma escrita concluída um instante antes. Uma leitura fortemente consistente (opt-in) garante o dado mais atualizado, ao custo de mais capacidade e latência.

TeoriaUma leitura fortemente consistente consome 2x os RCUs de uma eventualmente consistente equivalente, e não está disponível em Global Secondary Indexes (GSIs são sempre eventualmente consistentes, por definição da propagação assíncrona entre tabela base e índice).
Dia a diaUm saldo de carteira digital deve usar leitura fortemente consistente logo após um débito; já um contador de "curtidas" num post pode tolerar leitura eventualmente consistente sem problema perceptível.
AWS CLI
aws dynamodb get-item --table-name Contas \
  --key '{"contaId":{"S":"c-1"}}' \
  --consistent-read
Replicação multi-AZ e Global TablesAvançado
Alta disponibilidade dentro e entre regiões

Toda tabela do DynamoDB replica automaticamente seus dados em múltiplas zonas de disponibilidade (AZs) dentro da região, sem qualquer configuração — é o comportamento padrão do serviço. Para replicação entre regiões, existe o recurso Global Tables.

TeoriaGlobal Tables oferece replicação multi-região ativa-ativa: cada região tem uma réplica que aceita leituras e escritas, e as mudanças se propagam entre regiões usando o mecanismo interno do DynamoDB Streams. Conflitos de escrita concorrente em regiões diferentes são resolvidos por last-writer-wins baseado em timestamp.
Dia a diaUm app global com usuários em São Paulo e na Virgínia usa Global Tables para que cada região leia e escreva localmente, com baixa latência, enquanto os dados convergem entre regiões em segundo plano.
AWS CLI
# Adicionar uma réplica em outra região a uma tabela existente
aws dynamodb update-table \
  --table-name Usuarios \
  --replica-updates '[{"Create":{"RegionName":"us-east-1"}}]'
03

Modelagem & Acesso a Dados

Query, índices e o padrão single-table

Query vs. ScanIntermediário
A diferença que mais impacta performance e custo

Query busca itens por uma partition key específica (e, opcionalmente, uma condição sobre o sort key) — é eficiente porque acessa diretamente a partição correta. Scan percorre a tabela (ou índice) inteira, item por item, e só depois aplica qualquer filtro.

TeoriaUm FilterExpression em Query ou Scan é aplicado depois que os dados já foram lidos — ele reduz o que é devolvido para a aplicação, mas não reduz a capacidade (RCU) consumida na leitura. Um Scan com filtro que descarta 99% dos itens ainda cobra pela leitura dos 100%.
⚠️Scan em tabelas grandes é a causa mais comum de custo e latência inesperados no DynamoDB. Se uma consulta recorrente da aplicação depende de Scan, isso normalmente indica que falta um índice (GSI/LSI) desenhado para esse access pattern.
Python — boto3
# Query — eficiente, usa a partition key
resp = table.query(
    KeyConditionExpression=Key("clienteId").eq("c-1")
)

# Scan — lê a tabela inteira, filtra depois
resp = table.scan(
    FilterExpression=Attr("status").eq("pendente")
)
Índices secundários: GSI vs. LSIAvançado
Consultando por atributos fora da chave primária

Um Global Secondary Index (GSI) tem sua própria partition key (e sort key opcional), diferente da tabela base, e pode ser criado ou removido a qualquer momento. Um Local Secondary Index (LSI) mantém a mesma partition key da tabela base, mas com um sort key alternativo — e só pode ser definido na criação da tabela.

TeoriaGSIs têm capacidade de leitura/escrita própria (no modo Provisioned) e leitura sempre eventualmente consistente, já que os dados são propagados de forma assíncrona da tabela base. LSIs compartilham a capacidade da tabela base e suportam leitura fortemente consistente, mas têm uma restrição importante: o total de itens com a mesma partition key, somando tabela base + todos os LSIs, não pode passar de 10 GB.
Dia a diaUma tabela de Pedidos (chave por cliente) ganha um GSI com partition key status, permitindo consultar "todos os pedidos pendentes do sistema inteiro" — algo que a chave primária original não permite fazer sem um Scan.
AWS CLI
# Adicionar um GSI a uma tabela existente
aws dynamodb update-table --table-name Pedidos \
  --attribute-definitions AttributeName=status,AttributeType=S \
  --global-secondary-index-updates \
  '[{
    "Create": {
      "IndexName": "status-index",
      "KeySchema": [{"AttributeName":"status","KeyType":"HASH"}],
      "Projection": {"ProjectionType":"ALL"}
    }
  }]'
Single-table designAvançado
Modelar a partir dos access patterns, não das entidades

Single-table design é o padrão de modelar múltiplos tipos de entidade (usuários, pedidos, produtos) numa única tabela, usando nomes de chave genéricos (tipicamente PK e SK) "sobrecarregados" com prefixos que identificam o tipo de item — permitindo satisfazer vários access patterns com o menor número possível de requisições.

TeoriaA modelagem relacional tradicional começa pelas entidades e normaliza; a modelagem no DynamoDB começa pelos access patterns — as consultas exatas que a aplicação vai fazer — e desenha a chave primária e os índices para atendê-las diretamente, muitas vezes duplicando dados de propósito (denormalização) para evitar JOINs, que o DynamoDB não suporta nativamente. Nem toda aplicação precisa de single-table: quando os access patterns são simples, poucos ou pouco relacionados entre si, várias tabelas simples continuam sendo uma escolha válida e mais fácil de entender.
Dia a diaNuma única tabela, o item PK=USER#123, SK=PROFILE guarda o perfil do usuário e PK=USER#123, SK=ORDER#456 guarda um pedido dele — uma única Query com PK=USER#123 traz o perfil e todos os pedidos numa só chamada.
Exemplo de item
// Perfil do usuário
{ PK: "USER#123", SK: "PROFILE", nome: "Ana", email: "ana@ex.com" }

// Pedido do mesmo usuário — mesma partition key, sort key diferente
{ PK: "USER#123", SK: "ORDER#456", total: 149.90, status: "enviado" }
DynamoDB StreamsAvançado
Reagindo a mudanças em tempo real

O DynamoDB Streams, quando habilitado numa tabela, captura em ordem cada modificação item-level (INSERT, MODIFY, REMOVE), retida por 24 horas — funcionando como um log de mudanças que outras partes do sistema podem consumir.

TeoriaO uso mais comum é acionar uma função AWS Lambda automaticamente a cada mudança, processando o evento de forma assíncrona e desacoplada da escrita original — a aplicação que escreveu no DynamoDB não precisa saber quem consome o Stream. É também o mecanismo interno que sustenta o Global Tables (fase 2).
Dia a diaToda vez que um produto muda de preço na tabela principal, uma Lambda acionada pelo Stream atualiza o índice de busca (OpenSearch) usado pela busca do site, sem que o serviço de catálogo precise conhecer o serviço de busca.
AWS CLI
# Habilitar Streams numa tabela (imagem nova + antiga do item)
aws dynamodb update-table --table-name Produtos \
  --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
TTL — expiração automáticaIntermediário
Limpeza de itens sem job manual

O TTL (Time to Live) usa um atributo do tipo Number, guardando um timestamp Unix (epoch, em segundos), para marcar quando um item deve expirar. O DynamoDB remove itens expirados automaticamente em segundo plano.

TeoriaA remoção não é instantânea no momento exato da expiração — é feita por um processo de background que tipicamente exclui o item dentro de 48 horas após o timestamp de expiração, e não consome capacidade de escrita (WCU) da tabela. Itens já expirados, mas ainda não fisicamente removidos, também não aparecem em Query/Scan por padrão.
Dia a diaSessões de login com expiração de 24h guardam expiraEm = agora + 86400 num atributo TTL, e o DynamoDB limpa as sessões vencidas sozinho, sem um job agendado de limpeza.
AWS CLI
# Habilitar TTL apontando para o atributo de expiração
aws dynamodb update-time-to-live --table-name Sessoes \
  --time-to-live-specification Enabled=true,AttributeName=expiraEm
04

Operação, Segurança & Casos de Uso

Transações, cache, backup, custo e quando usar

Transações ACIDAvançado
TransactWriteItems e TransactGetItems

Uma escrita num único item já é atômica por padrão no DynamoDB. Quando a operação precisa alterar múltiplos itens (na mesma tabela ou em tabelas diferentes) de forma tudo-ou-nada, existem as transações explícitas TransactWriteItems (escrita) e TransactGetItems (leitura).

TeoriaUma transação de escrita suporta até 100 ações (ou 4 MB de dados), e garante isolamento ACID: ou todas as ações são aplicadas, ou nenhuma é. O custo é real — uma operação transacional consome o dobro da capacidade (RCU/WCU) de uma operação equivalente não-transacional, porque o DynamoDB faz duas fases internamente (preparar e confirmar).
⚠️Assim como transações multi-documento em outros bancos, use transações apenas quando a atomicidade entre itens é realmente necessária — ex: transferência entre duas contas. Para a maioria dos casos, um bom design de item único já resolve.
AWS CLI
# Transferência atômica entre duas contas
aws dynamodb transact-write-items --transact-items '[
  {
    "Update": {
      "TableName": "Contas",
      "Key": {"contaId": {"S": "A"}},
      "UpdateExpression": "SET saldo = saldo - :v",
      "ExpressionAttributeValues": {":v": {"N": "100"}}
    }
  },
  {
    "Update": {
      "TableName": "Contas",
      "Key": {"contaId": {"S": "B"}},
      "UpdateExpression": "SET saldo = saldo + :v",
      "ExpressionAttributeValues": {":v": {"N": "100"}}
    }
  }
]'
DAX — cache em memóriaAvançado
De milissegundos para microssegundos

O DynamoDB Accelerator (DAX) é um cluster de cache em memória totalmente gerenciado, compatível com a API do DynamoDB — trocar chamadas normais pelo client do DAX exige mudança mínima de código, e reduz leituras de latência de milissegundos para microssegundos em cargas de leitura intensiva.

TeoriaDAX é um cache write-through: escritas feitas através do client do DAX atualizam o cache e a tabela juntas. Porém, escritas feitas diretamente na tabela por fora do DAX (outro serviço, console, outra aplicação) não invalidam o cache automaticamente — o item em cache só se atualiza quando o TTL do item no DAX expira. Isso importa ao decidir onde o DAX se encaixa numa arquitetura com múltiplos escritores.
Dia a diaUm catálogo de produtos de e-commerce, lido milhares de vezes mais do que é escrito, coloca o DAX na frente da tabela para absorver a maior parte das leituras sem tocar no DynamoDB a cada requisição.
Backup, restore e Point-in-Time RecoveryAvançado
Recuperação de dados sem impacto de performance

Um backup sob demanda é uma cópia completa da tabela, feita a qualquer momento, sem impacto na performance de leitura/escrita, e retida até ser excluída manualmente. O Point-in-Time Recovery (PITR) mantém backups contínuos, permitindo restaurar a tabela para qualquer segundo dos últimos 35 dias.

TeoriaTanto o backup sob demanda quanto o PITR restauram para uma tabela nova — nunca sobrescrevem a tabela original no lugar. Isso é intencional: evita perder dados atuais por engano durante uma recuperação, mas exige um passo extra de "trocar" a aplicação para a tabela restaurada (ou migrar os dados de volta) depois do restore.
AWS CLI
# Habilitar PITR
aws dynamodb update-continuous-backups --table-name Pedidos \
  --point-in-time-recovery-specification PointInTimeRecoveryEnabled=true

# Restaurar para um instante específico (cria tabela nova)
aws dynamodb restore-table-to-point-in-time \
  --source-table-name Pedidos \
  --target-table-name Pedidos-restaurada \
  --restore-date-time 2026-08-20T03:00:00Z
Segurança: IAM e criptografiaAvançado
Controle de acesso granular e proteção de dados

O acesso ao DynamoDB é controlado por políticas IAM, que podem ser tão granulares quanto restringir um usuário a enxergar apenas itens ou atributos específicos. Dados ficam criptografados em repouso por padrão e em trânsito via TLS.

TeoriaA criptografia em repouso é sempre ativa e pode usar uma chave própria da AWS (owned key, sem custo), uma chave gerenciada pela AWS no KMS, ou uma chave gerenciada pelo próprio cliente (customer managed key), para quem precisa de controle total sobre rotação e revogação. Para controle de acesso fino, o IAM oferece condition keys específicas do DynamoDB, como dynamodb:LeadingKeys (restringe por valor de partition key) e dynamodb:Attributes (restringe quais atributos um item pode expor).
Dia a diaUm app multi-tenant usa dynamodb:LeadingKeys numa policy IAM para garantir que cada usuário autenticado só consiga ler/escrever itens cuja partition key comece com o próprio ID dele, mesmo que todos compartilhem a mesma tabela.
JSON — IAM Policy
{
  "Effect": "Allow",
  "Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
  "Resource": "arn:aws:dynamodb:*:*:table/Usuarios",
  "Condition": {
    "ForAllValues:StringEquals": {
      "dynamodb:LeadingKeys": ["${aws:userid}"]
    }
  }
}
Pricing — modelo de cobrançaIntermediário
O que compõe a fatura do DynamoDB

No modo Provisioned, a cobrança é por RCU/WCU provisionada por hora (com desconto possível via auto scaling que ajusta a capacidade à demanda real). No modo On-Demand, a cobrança é por milhão de unidades de leitura e escrita efetivamente consumidas, sem provisionamento prévio.

TeoriaAlém da capacidade, a fatura soma outros componentes conforme o uso: armazenamento (por GB-mês), leituras do DynamoDB Streams, escritas replicadas entre regiões no Global Tables, armazenamento de backups sob demanda e PITR (por GB-mês), horas de nó do DAX, e transferência de dados entre regiões/internet.
Dia a diaUm workload com padrão de tráfego previsível (ex: mais acesso em horário comercial) costuma sair mais barato em Provisioned com auto scaling do que em On-Demand constante — vale simular os dois modos com a calculadora oficial antes de decidir.
Quando usar (e quando não usar) DynamoDBAvançado
Casos de uso reais e os limites do modelo

DynamoDB brilha quando os access patterns são conhecidos e estáveis e a aplicação precisa de latência de milissegundos previsível em qualquer escala. Ele não substitui um banco relacional ou um data warehouse para todos os casos.

Bons casos de usoCarrinhos de compra e sessões de usuário, perfis e preferências, leaderboards de jogos, estado de dispositivos IoT, backends serverless (API Gateway + Lambda + DynamoDB), metadados de sistemas de alta escala onde a consulta principal é sempre "buscar pela chave conhecida".
⚠️Quando evitar: consultas ad-hoc e analíticas com JOINs e agregações variadas, relatórios de BI tradicionais, ou fases iniciais de um produto em que os access patterns ainda não estão claros — nesses casos, a flexibilidade de um modelo relacional normalizado costuma valer mais do que o ganho de performance do DynamoDB.
Dia a diaUma loja online usa DynamoDB para o carrinho de compras em tempo real (baixa latência, access pattern simples) e um data warehouse separado para os relatórios históricos de vendas (consultas analíticas variadas) — os dois convivem, cada um resolvendo o problema para o qual foi desenhado.
05

Modelagem de Dados & Performance

Técnicas avançadas de modelagem e um roteiro prático de otimização

Sparse IndexesAvançado
Índices que só existem quando o atributo existe

Um item só entra num Global Secondary Index se ele tiver todos os atributos que compõem a chave do índice. Um sparse index aproveita isso de propósito: criando um GSI cuja partition key é um atributo que só existe em alguns itens da tabela, o índice acaba contendo só o subconjunto relevante.

TeoriaSem sparse index, filtrar "todos os pedidos pendentes" exigiria um GSI com status como chave contendo todos os pedidos (pendentes, enviados, entregues) — um índice grande, a maior parte inútil para essa consulta. Adicionando um atributo separado, como pendenteFlag, que só é gravado quando status = "pendente" (e removido com REMOVE quando o pedido muda de status), o GSI sobre esse atributo fica pequeno e barato, contendo só o que interessa.
Dia a diaUma fila de tarefas pendentes de um sistema de suporte usa um sparse index sobre aguardandoTriagem — só os tickets realmente pendentes aparecem no índice, mesmo numa tabela com milhões de tickets já resolvidos.
AWS CLI
# O atributo só é gravado quando o pedido está pendente
aws dynamodb update-item --table-name Pedidos \
  --key '{"clienteId":{"S":"c-1"},"pedidoId":{"S":"p-1"}}' \
  --update-expression "SET pendenteFlag = :p" \
  --expression-attribute-values '{":p":{"S":"1"}}'

# Ao sair do estado pendente, remove o atributo — o item some do índice
aws dynamodb update-item --table-name Pedidos \
  --key '{"clienteId":{"S":"c-1"},"pedidoId":{"S":"p-1"}}' \
  --update-expression "REMOVE pendenteFlag"
Write sharding — distribuindo hot keysAvançado
Quando uma única partition key concentra escrita demais

Quando um único valor de partition key recebe escrita desproporcional (um contador global, um item viral), a técnica de write sharding anexa um sufixo (aleatório ou calculado) à chave, espalhando as escritas entre várias partições internas em vez de concentrar tudo numa só.

TeoriaO trade-off é explícito: escrever fica mais barato e resistente a throttling, mas ler o valor "total" fica mais caro, porque a aplicação precisa ler todos os N shards e somar os valores (com BatchGetItem, por exemplo) em vez de um único GetItem. Essa técnica só vale a pena quando o padrão de escrita realmente teria uma hot partition sem ela — aplicar sharding numa chave que já é bem distribuída só adiciona complexidade sem ganho.
Dia a diaO contador de visualizações de um vídeo que viraliza usa 10 shards (video#123#0 … video#123#9); cada visualização incrementa um shard escolhido aleatoriamente, e o total exibido na tela soma os 10 valores — que pode até ser cacheado por alguns segundos para não somar a cada exibição.
Python — boto3
# Escrita — sufixo aleatório entre 0 e 9
shard = random.randint(0, 9)
table.update_item(
    Key={"PK": f"video#123#{shard}"},
    UpdateExpression="ADD visualizacoes :um",
    ExpressionAttributeValues={":um": 1}
)

# Leitura — agrega os 10 shards
chaves = [{"PK": f"video#123#{i}"} for i in range(10)]
resp = dynamodb.batch_get_item(RequestItems={"Videos": {"Keys": chaves}})
total = sum(item["visualizacoes"] for item in resp["Responses"]["Videos"])
Adaptive CapacityIntermediário
A rede de segurança automática contra desequilíbrio de tráfego

O Adaptive Capacity é um mecanismo interno, sempre ativo, que redistribui automaticamente capacidade não utilizada de partições "frias" para partições "quentes" da mesma tabela, sem qualquer configuração da sua parte.

TeoriaDesde que passou a ser instantâneo (a versão anterior levava minutos para reagir), o Adaptive Capacity ajuda a absorver picos moderados de tráfego desigual entre partições sem gerar throttling perceptível. Ainda assim, não substitui um bom design de partition key: ele suaviza desequilíbrios, mas uma chave extremamente concentrada (um único valor recebendo quase todo o tráfego da tabela) continua sendo um gargalo que só o write sharding ou uma modelagem melhor resolvem de verdade.
Projeções: KEYS_ONLY/INCLUDE/ALL vs. ProjectionExpressionIntermediário
Duas formas de limitar atributos — só uma reduz custo de índice

Ao criar um GSI/LSI, a projeção do índice (KEYS_ONLY, INCLUDE ou ALL) define quais atributos são copiados da tabela base para o índice. Já o ProjectionExpression é um parâmetro passado numa chamada de leitura para escolher quais atributos retornar na resposta.

TeoriaEssa distinção evita um engano comum de otimização: ProjectionExpression reduz o volume de dados trafegado de volta para a aplicação, mas a capacidade consumida (RCU) é calculada pelo tamanho do item lido internamente, antes da projeção ser aplicada — não economiza RCU. Já a projeção do índice em KEYS_ONLY ou INCLUDE reduz de fato o que é armazenado e lido do índice, porque atributos fora da projeção simplesmente não existem ali — essa é a alavanca real de economia quando o índice só precisa responder "quais chaves existem" ou alguns poucos atributos extras.
AWS CLI
# Índice enxuto — só guarda as chaves, mais barato de manter e consultar
aws dynamodb update-table --table-name Pedidos \
  --attribute-definitions AttributeName=status,AttributeType=S \
  --global-secondary-index-updates \
  '[{
    "Create": {
      "IndexName": "status-index-leve",
      "KeySchema": [{"AttributeName":"status","KeyType":"HASH"}],
      "Projection": {"ProjectionType":"KEYS_ONLY"}
    }
  }]'

# ProjectionExpression — reduz o payload retornado, não o RCU consumido
aws dynamodb query --table-name Pedidos \
  --key-condition-expression "clienteId = :c" \
  --expression-attribute-values '{":c":{"S":"c-1"}}' \
  --projection-expression "pedidoId, status"
Batch operationsIntermediário
BatchGetItem e BatchWriteItem — menos round-trips

BatchGetItem busca até 100 itens (por chave conhecida) e BatchWriteItem grava/remove até 25 itens (até 16 MB) numa única chamada — reduzindo o número de round-trips de rede em comparação com chamar GetItem/PutItem item a item.

TeoriaDiferente de TransactWriteItems, uma operação em lote não é atômica: alguns itens podem ter sucesso e outros falhar (por throttling, por exemplo), retornando em UnprocessedItems (escrita) ou UnprocessedKeys (leitura). A aplicação precisa reenviar esses itens, idealmente com backoff exponencial, até a lista vir vazia.
⚠️Ignorar UnprocessedItems é um bug silencioso comum: a chamada retorna sucesso (HTTP 200), mas parte dos itens pode não ter sido gravada.
Python — boto3
# Grava em lote e reprocessa o que falhar, com backoff
resp = dynamodb.batch_write_item(RequestItems={"Produtos": itens_para_gravar})
pendentes = resp.get("UnprocessedItems", {})
espera = 0.1
while pendentes:
    time.sleep(espera)
    resp = dynamodb.batch_write_item(RequestItems=pendentes)
    pendentes = resp.get("UnprocessedItems", {})
    espera = min(espera * 2, 5)
Paginação e Parallel ScanIntermediário
Lidando com resultados grandes

Toda resposta de Query ou Scan é limitada a 1 MB de dados lidos. Quando há mais resultados, a resposta traz um LastEvaluatedKey que deve ser passado como ExclusiveStartKey na próxima chamada para continuar de onde parou.

TeoriaParallel Scan divide a tabela em N segmentos lógicos (TotalSegments) e processa cada um em paralelo com workers separados, cada um informando seu Segment. Isso acelera um Scan completo (útil em exportações e migrações pontuais), mas consome capacidade de forma agressiva e simultânea — rodar em produção sem throttling do lado da aplicação pode competir diretamente com o tráfego real e causar throttling generalizado na tabela.
Python — boto3
# Paginação simples
chave = None
while True:
    kwargs = {"ExclusiveStartKey": chave} if chave else {}
    resp = table.scan(**kwargs)
    processa(resp["Items"])
    chave = resp.get("LastEvaluatedKey")
    if not chave:
        break

# Parallel Scan — 4 segmentos processados de forma independente
resp = table.scan(Segment=0, TotalSegments=4)
Tamanho de item e custoIntermediário
RCU/WCU são calculados pelo tamanho do item

Como o consumo de RCU/WCU é calculado com base no tamanho do item (arredondado para cima em blocos de 4 KB ou 1 KB), reduzir o tamanho médio dos itens reduz custo e aumenta o throughput possível por partição.

TeoriaNomes de atributos contam para o tamanho do item — em tabelas com altíssimo volume de escrita, usar nomes curtos (n em vez de nomeCompleto) é uma otimização real, embora custe legibilidade. Para campos grandes de texto ou JSON que não precisam ser consultados diretamente, comprimir (gzip) antes de armazenar como Binary reduz o tamanho do item — ao custo de não conseguir mais filtrar ou fazer FilterExpression sobre esse campo sem descomprimir primeiro.
💡Use --return-consumed-capacity TOTAL (CLI) ou ReturnConsumedCapacity="TOTAL" (SDK) durante o desenvolvimento para medir o custo real de cada operação antes de otimizar às cegas.
Monitoramento — CloudWatch e Contributor InsightsAvançado
Enxergando throttling e hot keys antes que virem incidente

O DynamoDB publica métricas no CloudWatch automaticamente — consumo de capacidade (ConsumedReadCapacityUnits/ConsumedWriteCapacityUnits), requisições throttled (ThrottledRequests) e latência (SuccessfulRequestLatency).

TeoriaEssas métricas são agregadas por tabela/índice, então não mostram sozinhas qual partition key está causando um hot spot. Para isso existe o Contributor Insights for DynamoDB, um recurso opcional que identifica as chaves mais acessadas ("top keys") e as que mais sofrem throttling, sem exigir nenhuma instrumentação extra na aplicação.
Dia a diaUm pico de latência reportado pelos usuários é investigado olhando ThrottledRequests no CloudWatch; ao confirmar throttling, o Contributor Insights aponta exatamente qual cliente ou produto está concentrando as chamadas, direcionando a correção (write sharding, cache, ou revisão da partition key).
AWS CLI
# Habilitar Contributor Insights numa tabela
aws dynamodb enable-contributor-insights --table-name Pedidos

# Consultar requisições throttled na última hora
aws cloudwatch get-metric-statistics \
  --namespace AWS/DynamoDB --metric-name ThrottledRequests \
  --dimensions Name=TableName,Value=Pedidos \
  --start-time $(date -u -d '-1 hour' +%FT%TZ) --end-time $(date -u +%FT%TZ) \
  --period 300 --statistics Sum
Checklist prático de otimizaçãoAvançado
Resumo de tudo, na ordem em que costuma valer a pena aplicar

Junta os tópicos das cinco fases num roteiro prático — a ordem importa: modelagem correta evita boa parte dos problemas de performance antes mesmo de precisar de otimização fina.

01Modele a partir dos access patterns da aplicação, não das entidades — defina as consultas antes da chave primária
02Escolha uma partition key de alta cardinalidade e acesso uniforme; se um valor concentrar tráfego, aplique write sharding
03Prefira sempre Query a Scan; se um Scan completo for inevitável, use Parallel Scan com throttling do lado da aplicação
04Crie GSIs com a projeção mínima necessária (KEYS_ONLY/INCLUDE), inclusive sparse indexes quando a consulta filtra por um subconjunto pequeno
05Agrupe operações com Batch quando não precisar de atomicidade, e com Transact só quando precisar
06Coloque o DAX na frente de cargas de leitura muito mais frequentes que escrita
07Meça antes de otimizar: use ReturnConsumedCapacity e reduza o tamanho médio dos itens onde fizer diferença real
08Monitore continuamente com CloudWatch e Contributor Insights — não só no lançamento, hot keys podem surgir meses depois com o crescimento da base