Iniciante7 min de leitura

Por que um Scan do DynamoDB É Lento e Caro

Um Scancada item da tabela e só filtra depois. É a operação que você recorre por memória muscular de SQL, e a que silenciosamente engorda sua conta enquanto piora sua latência além da caixa RDS que você deixou para trás.

Por que meu Scan do DynamoDB é lento e caro?

Um Scan lê cada item da tabela antes de a FilterExpression ser executada, então você paga para ler a tabela inteira independentemente de quantas linhas voltam, e fica mais lento conforme a tabela cresce. A correção é quase sempre um Query chaveado — modele o padrão de acesso em torno de uma chave para que o DynamoDB toque uma partição em vez de tudo.

  • Um Scan lê a tabela inteira, toda vez. O tamanho, não a contagem do seu resultado, decide o que você paga e quanto tempo leva.
  • A FilterExpression é uma mentira sobre custo. Ela roda depois que a leitura é medida, então retornar 12 itens pode cobrar pela leitura de 12 milhões.
  • Um Scan fica mais lento conforme você cresce. Um Query chaveado permanece plano — toca uma partição não importa quão grande a tabela fique.
  • A correção é quase sempre modelagem, não tuning. Se você faz Scan para responder uma pergunta rotineira, está faltando uma chave.

O que um Scan de fato faz

Vindo do SQL, SELECT * FROM events WHERE type = 'checkout' parece de graça — o motor tem um índice, ou não tem, mas de qualquer jeito você recebe linhas de volta. No DynamoDB não há query planner decidindo isso por você.

Um Scan percorre a tabela inteira sequencialmente, 1 MB de cada vez, e entrega cada página à sua FilterExpression. O que o filtro rejeita ainda é lido, ainda é medido, e ainda está na sua conta. (AWS: Scanning tables)

Essa é a cilada. O filtro parece uma cláusula WHERE, mas muda o conjunto de resultados, nunca o custo. Um Scan consome a mesma capacidade de leitura havendo ou não um filtro presente. (AWS: Scanning tables)

Conte as unidades de leitura

O DynamoDB mede leituras em unidades de capacidade de leitura (RCUs). Uma RCU compra uma única leitura fortemente consistente de um item de até 4 KB; leituras eventualmente consistentes custam metade disso. Itens maiores arredondam para o próximo 4 KB. (AWS: Read/write capacity mode)

Pegue uma tabela de analytics, ProductEvents. Cada linha é um evento rastreado:

PK  = "TENANT#acme"
SK  = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytes

Digamos que ela contenha 2.000.000 eventos, cada um ~1 KB, todos sob um único tenant ocupado. Você quer os checkouts de hoje. A jogada reflexiva:

Scan ProductEvents
FilterExpression: eventType = "checkout"

Esse filtro pode retornar 40 linhas. Mas o Scan leu todos os 2.000.000 de itens primeiro. A ~1 KB cada (1 RCU por 4 KB, eventualmente consistente ≈ 0,5 RCU por 4 KB), você mediu algo em torno de 250.000 RCUs — e paginou por ~500 MB de dados — para devolver 40 itens.

Agora modele o padrão de acesso como uma chave e faça Query em vez disso:

Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"

Isso lê só a fatia correspondente de uma partição. Se essas 40 linhas de checkout mais os outros eventos do dia chegam a ~2 MB, você paga por ~2 MB de leituras, não 500 MB. Mesma resposta, uma fração minúscula do custo — e a latência permanece plana conforme a tabela cresce.

Scan vs Query, medido

Scan + filtroQuery chaveado
LeiturasCada item da tabelaUma partição, estreitada por SK
Capacidade cobradaTabela inteira, antes do filtroSó os itens na sua fatia
Nosso exemplo~250.000 RCUs (~500 MB)algumas centenas de RCUs (~2 MB)
LatênciaCresce com o tamanho da tabelaPlana conforme a tabela cresce
Contagem do resultadoNão decide nada sobre o custoCorresponde ao que você paga

A lição que a tabela codifica: num Scan, sua contagem de resultado e sua conta não têm relação. Num Query, elas se acompanham.

Decida antes de fazer Scan

A maioria dos Scans acidentais vem de uma pergunta: consigo nomear a partição de que preciso? Se sim, é um Query. Se não, a correção é uma chave, não um filtro maior.

Aqui está a decisão em forma de fluxo.

SimNãoSimNãoPrecisa ler itensSabe a chave de partição?Query uma partiçãoUm GSI pode chaveá-la?Adicione um GSI, depois QueryScan último recurso

O caminho quase sempre termina em Query; você só cai para Scan quando nenhuma chave — presente ou adicionável — encaixa no padrão de acesso.

Se o padrão é real e recorrente mas a tabela base não consegue chaveá-lo, esse é o sinal de adicionar um Índice Secundário Global para que a pergunta vire um Query. Modelar suas chaves em torno dos seus padrões de acesso de antemão é o jogo inteiro — veja single-table design.

Escreva a consulta chaveada, não um filtro

Quando você de fato precisa de uma condição além da chave, construa-a deliberadamente em vez de despejar tudo numa FilterExpression. O DynamoDB Expression Builder gera a KeyConditionExpression e os placeholders de atributo para você, para que a chave de partição e a de ordenação façam o estreitamento — antes de o DynamoDB medir a leitura, não depois.

KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)

Quando um Scan de fato é aceitável

Um Scan não é proibido — é só o padrão errado. É a ferramenta certa quando você genuinamente quer dizer "ler tudo":

  • Exports avulsos ou backfills rodados à mão.
  • Tabelas minúsculas de config / lookup onde a tabela inteira tem alguns poucos KB.
  • Jobs de background que paginam a tabela completa de propósito. Divida-os entre workers com Segment / TotalSegments — um scan paralelo — em vez de um único rastreio sequencial longo. (AWS: Scanning tables)

E note que o PartiQL não te salva: SELECT * FROM ProductEvents WHERE eventType = 'checkout' sem um predicado de chave compila direto para um Scan. É a mesma cilada com roupa de SQL. (Veja Query vs Scan para o detalhamento completo.)

Quando você realmente precisa de analytics entre itens — um GROUP BY, um JOIN, um agregado que o DynamoDB não consegue expressar — o SQL Workbench do DynoTable os roda no lado do cliente sobre um conjunto de resultados limitado, em vez de martelar a tabela com um Scan completo.

Próximos passos

Estime o que cada padrão custa com a calculadora de capacidade, leia Query vs Scan para o contraste no nível da API, e baixe o DynoTable para rodá-los contra suas próprias tabelas e ver a capacidade consumida você mesmo.

Atualizado