Intermediário8 min de leitura

DynamoDB Estratégias de filtragem

"Filtrar" em DynamoDB significa quatro coisas diferentes usando a mesma palavra. Três restrinja os dados antes de serem lidos e cobrados; um — aquele chamado Filtro — restringe depois. Saber qual é qual é a maior parte da habilidade.

Como funciona a filtragem em DynamoDB?

DynamoDB tem quatro maneiras de filtrar e apenas uma é executada após a cobrança. Oescolhe uma partição, a chave de classificação restringe uma fatia e um índice esparso filtra por presença de atributo – todos os três reduzem o custo de leitura antes da medição. Um FilterExpression é executado após a leitura, diminuindo a resposta, mas nunca a conta.

  • é o filtro mais barato: ele escolhe a partição, então você nunca toque no resto da mesa.
  • filtra dentro de uma partição com begins_with, between, <, > — ainda antes do faturamento, ainda barato.
  • filtros por ausência: um item só aparece no índice se tem o atributo indexado, então o índice é o conjunto filtrado.
  • FilterExpression é a armadilha: ela é executada após DynamoDB medir a leitura, então isso reduz o tamanho da sua resposta, mas nunca a sua conta.

Configure 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"

Cada produto também carrega price, inStock (um booleano) e clearanceAt (um carimbo de data/hora unix, presente apenas em itens marcados para liberação). Itens em um departamento compartilha uma partição, classificada por ID do 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 pelo qual você pagará para sempre.

Filtrar por chave de partição

"Dê-me todos os produtos da 'cozinha'." A chave de partição responde diretamente:

Query  PK = "DEPT#kitchen"

DynamoDB lê exatamente uma partição. Nada mais na mesa é tocado ou faturado. Este é o único filtro gratuito no sentido que importa - é o diferença entre Query e Scan.

Vindo de SQL, parece ao contrário: não há WHERE departamento = 'cozinha' digitalizando um índice, basta nomear a partição. Se você não consegue nomeá-lo, isso é um problema de modelagem, não um problema de consulta.

Filtrar por chave de classificação

"Dê-me produtos de cozinha de PROD#00100 em diante." A chave de classificação restringe dentro a partição, e faz isso antes que a leitura seja medida:

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

As condições da chave de classificação são limitadas propositalmente: =, <, <=, >, >=, entre e begins_with. Sem OR, sem predicado arbitrário.

Essa restrição é o que mantém a leitura direcionada - DynamoDB percorre um caminho contíguo fatia, não a partição inteira.

A alavanca aqui é como você codifica a chave de classificação. Se o seu padrão for "por preço band", uma chave de classificação PROD#<id> não ajudará - você incluiria o preço na chave.

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

Filtrar por índice esparso

"Dê-me tudo que está atualmente em liquidação." A maioria dos produtos não é, então você não quero ler o catálogo para encontrar os poucos que existem.

Um índice esparso resolve isso por ausência. UMapenas contém um item se esse item tiver ambos os atributos-chave do índice.

Defina um sinalizador constante clearance = "CLEARANCE" como a chave de partição GSI - escrito apenas em itens de liquidação - com clearanceAt como chave de classificação e o índice não contém mais nada.

AWS explica isso: um índice secundário global contém apenas itens que possuem o atributos-chave do índice, portanto, os itens que faltam o atributo-chave simplesmente não são propagado ([AWS — Aproveite as vantagens de índices esparsos][esparsos]).

SimNoMesa base todos os produtosHas clearanceAt?Replicado para ClearanceIndexNão está no índiceQuery o índice = apenas itens emliquidação

Agora a consulta lê apenas os itens de liquidação, faturados apenas por eles:

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

O filtro aconteceu quando você escreveu os dados - escolhendo se deseja definir clearanceAt em absoluto. O índice é o conjunto filtrado. Veja GSI vs LSI para qual tipo de índice se ajusta.

Filtrar com FilterExpression

“Dê-me produtos de cozinha que estejam em estoque.” inStock não é um atributo chave, então você pega um FilterExpression:

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

DynamoDB lê cada item na partição cozinha, mede a capacidade para todos eles, e então descarta os que estão fora de estoque.

AWS afirma que uma expressão de filtro é "aplicada após o término de Query, mas antes que os resultados sejam retornados" e "um Query consome a mesma quantidade de leitura capacidade, independentemente de uma expressão de filtro estar presente" — você já pago pela leitura completa (AWS — Filtrar expressões para Query).

Então se a cozinha tem 10.000 produtos e 12 estão em estoque, você paga para ler 10.000. A resposta é pequena; a conta não é. FilterExpression reduz a carga útil cruzando o fio, nunca a leitura.

Há uma segunda vantagem mais nítida: 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 conjunto LastEvaluatedKey - DynamoDB leia um megabyte completo, não correspondeu a nada, entregou a você um array vazio. Você continua paginando e você pagou por cada página vazia.

Construa a expressão - nomes, valores e o escape correto da palavra reservada - com o DynamoDB Expression Builder então o Os espaços reservados #inStock/:val estão corretos na primeira tentativa.

O construtor abaixo está predefinido para Scan com FilterExpression — o exato antipadrão acima. Observe que o filtro é executado em toda a tabela, não em uma fatia chave:

Monte sua requisição
Código gerado
new ScanCommand({
  "TableName": "AuditLog",
  "FilterExpression": "#filter0 = :filterValue0",
  "ExpressionAttributeNames": {
    "#filter0": "action"
  },
  "ExpressionAttributeValues": {
    ":filterValue0": {
      "S": "delete"
    }
  }
})

Compare os quatro

Quando filtraReduz o custo de leitura?Poder predicadoCusto de configuração
Chave de partiçãoAntes de lerSim — uma partiçãoApenas igualdadeGrátis (é a chave)
Chave de classificaçãoAntes de lerSim – uma fatiaFaixa / begins_withDesign de chave de classificação
Índice esparsoAntes de lerSim — somente índicePresença de um atributoExtra GSI + custo de gravação
FilterExpressionDepois de lerNãoQuase qualquer condiçãoNenhum

Leia a tabela de cima a baixo: o poder do predicado aumenta, o controle de custos aumenta para baixo. FilterExpression pode expressar qualquer coisa precisamente porque roda em itens já lidos - é a mesma razão pela qual você não pode economizar dinheiro.

Veja no DynoTable

Quando você executa um Query com um filtro, a lacuna entre os itens lidos e os itens retornado é a história toda. DynoTable mostra os itens digitalizados ao lado dos itens retornado como fluxos de leitura filtrados - portanto, um filtro que lê silenciosamente toda a partição fica visível, não escondido em sua fatura mensal.

Para perguntas genuínas sobre itens cruzados, um filtro não consegue responder — "preço médio por departamento", "produtos em estoque associados às suas avaliações" — DynoTable's SQL Workbench executa GROUP BY, JOIN e agrega o lado do cliente em um limite conjunto de resultados, em vez de compilar para um Scan de toda a tabela.

Armadilhas e próximos passos

  • Não use FilterExpression como seu caminho de acesso principal. Se um padrão for comum, modele-o em uma chave ou em um índice esparso. Um filtro é para o último pouco um pouco de estreitamento, não a maior parte.
  • Observar páginas vazias. Uma consulta filtrada pode retornar uma página por um longo tempo nada. Honre LastEvaluatedKey; não presuma que uma página vazia significa "pronto".
  • Um índice esparso não é gratuito. Custa capacidade de gravação e armazenamento para cada item que cai nele — barato quando o atributo é raro, menos quando não é.

Estime quanto custará realmente uma leitura filtrada com o calculadora de preços e tente DynoTable para observar a capacidade consumida em relação às linhas retornadas em suas próprias mesas.

Atualizado