Intermediário8 min de leitura

Estratégias de Filtragem no DynamoDB

"Filtragem" no DynamoDB significa quatro coisas diferentes usando a mesma palavra. Três estreitam os dados antes de serem lidos e cobrados; uma — a que se chama Filter — os estreita depois. Saber qual é qual é a maior parte da habilidade.

Como funciona a filtragem no DynamoDB?

O DynamoDB tem quatro formas de filtrar, e só uma roda depois que você é cobrado. A chave de partição escolhe uma partição, a chave de classificação estreita uma fatia, e um índice esparso filtra por presença de atributo — as três cortam seu custo de leitura antes da medição. Uma FilterExpression roda depois da leitura, então ela encolhe a resposta mas nunca a conta.

  • Chave de partição é o filtro mais barato: ela escolhe a partição, então você nunca toca o resto da tabela.
  • Chave de classificação filtra dentro de uma partição com begins_with, between, <, > — ainda antes da cobrança, ainda barato.
  • Índice esparso filtra por ausência: um item só aparece no índice se tiver o atributo indexado, então o índice é o conjunto filtrado.
  • FilterExpression é a cilada: ele roda depois que o DynamoDB mede a leitura, então corta o tamanho da sua resposta mas nunca a sua conta.

Monte o exemplo

Um catálogo de produtos. Uma tabela, chave de partição PK, chave de classificação SK:

PK = "DEPT#kitchen"   SK = "PROD#00194"

Todo produto também carrega price, inStock (um booleano) e clearanceAt (um unix timestamp, presente só em itens marcados para liquidação). Itens em um departamento compartilham uma partição, ordenados por id de produto.

Queremos quatro padrões de acesso. Cada um mapeia para uma estratégia de filtragem diferente — e a escolha errada em qualquer um deles é um Scan que você vai pagar para sempre.

Filtre por chave de partição

"Me dê todo produto em kitchen." A chave de partição responde isso diretamente:

Query  PK = "DEPT#kitchen"

O DynamoDB lê exatamente uma partição. Nada mais na tabela é tocado ou cobrado. Esse é o único filtro que é grátis no sentido que importa — é a diferença entre Query e Scan.

Vindo do SQL, isso parece de cabeça para baixo: não há um WHERE department = 'kitchen' fazendo scan de um índice, você apenas nomeia a partição. Se você não consegue nomeá-la, isso é um problema de modelagem, não um problema de consulta.

Filtre por chave de classificação

"Me dê produtos de kitchen a partir de PROD#00100." A chave de classificação estreita dentro da partição, e faz isso antes de a leitura ser medida:

Query  PK = "DEPT#kitchen"  AND  SK between "PROD#00100" AND "PROD#00200"

Condições de chave de classificação são limitadas de propósito: =, <, <=, >, >=, between e begins_with. Sem OR, sem predicado arbitrário.

Essa restrição é o que mantém a leitura mirada — o DynamoDB percorre uma fatia contígua, não a partição inteira.

A alavanca aqui é como você codifica a chave de classificação. Se o seu padrão é "por faixa de preço", uma chave de classificação PROD#<id> não ajuda — você assaria o preço na chave.

Essa é uma decisão de estratégia de chave de classificação, tomada em tempo de design, não em tempo de consulta.

Filtre por índice esparso

"Me dê tudo que está em liquidação no momento." A maioria dos produtos não está, então você não quer ler o catálogo para achar os poucos que estão.

Um índice esparso resolve isso por ausência. Um Global Secondary Index só contém um item se aquele item tiver ambos os atributos de chave do índice.

Defina a chave de partição do GSI como clearanceAt — presente só em itens de liquidação — e o índice não contém mais nada.

A AWS deixa isso claro: um GSI "só contém itens que têm os atributos indexados", então itens sem o atributo de chave simplesmente não são propagados (AWS — aproveite os índices esparsos).

SimNãoTabela base todos os produtosTem clearanceAt?Replicado para o ClearanceIndexFora do índiceConsultar o índice = itens deliquidação

Agora a consulta lê só os itens de liquidação, cobrada só por eles:

Query  ON ClearanceIndex   GSI_PK = "CLEARANCE"   (sorted by clearanceAt)

A filtragem aconteceu quando você escreveu os dados — ao escolher se definia clearanceAt ou não. O índice é o conjunto filtrado. Veja GSI vs LSI para qual tipo de índice encaixa.

Filtre com FilterExpression

"Me dê produtos de kitchen que estão em estoque." inStock não é um atributo de chave, então você recorre a uma FilterExpression:

Query  PK = "DEPT#kitchen"
Filter inStock = true

Eis a cilada. O DynamoDB lê todo item na partição kitchen, mede a capacidade de todos eles, e depois descarta os fora de estoque.

A regra oficial: uma expressão de filtro é "aplicada depois que um Query termina, mas antes de os resultados serem retornados", e "não consome nenhuma unidade de capacidade de leitura adicional" — você já pagou pela leitura completa (AWS — expressões de filtro para Query).

Então se kitchen tem 10.000 produtos e 12 estão em estoque, você paga para ler 10.000. A resposta é pequena; a conta não é. A FilterExpression encolhe o payload que cruza o fio, nunca a leitura.

Há uma segunda borda, mais afiada: a paginação é medida antes da filtragem. Uma página é 1 MB de itens lidos, não 1 MB de correspondências.

Um filtro pode retornar uma página vazia com um LastEvaluatedKey definido — o DynamoDB leu um megabyte completo, não correspondeu a nada, e te entregou um array vazio. Você continua paginando, e pagou por cada página vazia.

Monte a expressão — nomes, valores e o escape correto de palavras reservadas — com o DynamoDB Expression Builder para que os placeholders #inStock/:val estejam corretos na primeira tentativa.

Compare os quatro

Quando filtraCorta custo de leitura?Poder de predicadoCusto de montagem
Chave de partiçãoAntes da leituraSim — uma partiçãoSó igualdadeGrátis (é a chave)
Chave de classificaçãoAntes da leituraSim — uma fatiaIntervalo / begins_withDesign da chave de classificação
Índice esparsoAntes da leituraSim — só índicePresença de um atributoGSI extra + custo de escrita
FilterExpressionApós a leituraNãoQuase qualquer condiçãoNenhum

Leia a tabela de cima a baixo: o poder de predicado sobe, o controle de custo desce. A FilterExpression pode expressar qualquer coisa precisamente porque roda em itens já lidos — essa é a mesma razão pela qual ela não pode te poupar dinheiro.

Veja no DynoTable

Quando você roda um Query com um filtro, a lacuna entre itens lidos e itens retornados é a história toda. O DynoTable mostra a capacidade consumida ao lado da contagem de resultados — então um filtro lendo silenciosamente a partição inteira fica visível, não escondido na sua conta mensal.

Para perguntas genuínas entre itens que um filtro não pode responder — "preço médio por departamento", "produtos em estoque juntados às suas avaliações" — o SQL Workbench do DynoTable roda GROUP BY, JOIN e agregações no lado do cliente sobre um conjunto de resultados limitado, em vez de compilar para um Scan de tabela inteira.

Ciladas e próximos passos

  • Não use FilterExpression como seu caminho de acesso primário. Se um padrão é comum, modele-o em uma chave ou um índice esparso. Um filtro é para o último pedacinho de estreitamento, não para o grosso dele.
  • Atente para páginas vazias. Uma consulta filtrada pode paginar por muito tempo retornando nada. Respeite o LastEvaluatedKey; não assuma que uma página vazia significa "acabou".
  • Um índice esparso não é grátis. Ele custa capacidade de escrita e armazenamento para cada item que cai nele — barato quando o atributo é raro, menos quando não é.

Estime quanto uma leitura filtrada vai realmente custar com a calculadora de capacidade, e experimente o DynoTable para observar a capacidade consumida contra as linhas retornadas nas suas próprias tabelas.

Atualizado