Intermedio8 min di lettura

Strategie di Filtraggio in DynamoDB

"Filtrare" in DynamoDB significa quattro cose diverse che indossano la stessa parola. Tre restringono i dati prima che siano letti e fatturati; una — quella chiamata Filter — li restringe dopo. Sapere quale è quale è gran parte dell'abilità.

Come funziona il filtraggio in DynamoDB?

DynamoDB ha quattro modi di filtrare, e solo uno gira dopo che ti è stata addebitata la lettura. La chiave di partizione sceglie una partizione, la sort key restringe una fetta e uno sparse index filtra per presenza di un attributo — tutti e tre tagliano il costo delle letture prima della fatturazione. Una FilterExpression gira dopo la lettura, quindi riduce la dimensione della risposta ma mai il conto.

  • La chiave di partizione è il filtro più economico: sceglie la partizione, quindi non tocchi mai il resto della tabella.
  • La sort key filtra dentro una partizione con begins_with, between, <, > — sempre prima della fatturazione, sempre economico.
  • Lo sparse index filtra per assenza: un Item compare nell'indice solo se ha l'attributo indicizzato, quindi l'indice è l'insieme filtrato.
  • FilterExpression è la trappola: gira dopo che DynamoDB ha contato la lettura, quindi taglia la dimensione della risposta ma mai il tuo conto.

Imposta l'esempio

Un catalogo prodotti. Una tabella, chiave di partizione PK, sort key SK:

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

Ogni prodotto porta anche price, inStock (un boolean) e clearanceAt (un timestamp unix, presente solo sugli Item contrassegnati per liquidazione). Gli Item in un reparto condividono una partizione, ordinati per id prodotto.

Vogliamo quattro pattern di accesso. Ognuno mappa a una strategia di filtraggio diversa — e la scelta sbagliata su uno qualsiasi è uno Scan che pagherai per sempre.

Filtra per chiave di partizione

"Dammi ogni prodotto in kitchen." La chiave di partizione risponde direttamente:

Query  PK = "DEPT#kitchen"

DynamoDB legge esattamente una partizione. Nient'altro nella tabella viene toccato o fatturato. Questo è l'unico filtro gratuito nel senso che conta — è la differenza tra Query e Scan.

Venendo da SQL, sembra al contrario: non c'è un WHERE department = 'kitchen' che scansiona un indice, nomini la partizione e basta. Se non puoi nominarla, è un problema di modellazione, non un problema di query.

Filtra per sort key

"Dammi i prodotti kitchen da PROD#00100 in su." La sort key restringe dentro la partizione, e lo fa prima che la lettura sia contata:

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

Le condizioni sulla sort key sono limitate di proposito: =, <, <=, >, >=, between e begins_with. Niente OR, nessun predicato arbitrario.

Quel vincolo è ciò che mantiene mirata la lettura — DynamoDB percorre una fetta contigua, non l'intera partizione.

La leva qui è come codifichi la sort key. Se il tuo pattern è "per fascia di prezzo", una sort key PROD#<id> non aiuta — dovresti incorporare il prezzo nella chiave.

È una decisione di strategia delle sort key, presa in fase di design, non di query.

Filtra per sparse index

"Dammi tutto attualmente in liquidazione." La maggior parte dei prodotti non lo è, quindi non vuoi leggere il catalogo per trovare i pochi che lo sono.

Uno sparse index risolve questo per assenza. Un Global Secondary Index contiene un Item solo se quell'Item ha entrambi gli attributi chiave dell'indice.

Imposta la chiave di partizione del GSI a clearanceAt — presente solo sugli Item in liquidazione — e l'indice non contiene nient'altro.

AWS lo esplicita: un GSI "contiene solo Item che hanno gli attributi indicizzati", quindi gli Item privi dell'attributo chiave semplicemente non vengono propagati (AWS — Take advantage of sparse indexes).

NoTabella base tutti i prodottiHa clearanceAt?Replicato in ClearanceIndexNon nell'indiceQuery l'indice = solo Item inliquidazione

Ora la query legge solo gli Item in liquidazione, fatturati solo per loro:

Query  ON ClearanceIndex   GSI_PK = "CLEARANCE"   (ordinati per clearanceAt)

Il filtro è avvenuto quando hai scritto i dati — scegliendo se impostare clearanceAt o no. L'indice è l'insieme filtrato. Vedi GSI vs LSI per quale tipo di indice si adatta.

Filtra con FilterExpression

"Dammi i prodotti kitchen che sono in stock." inStock non è un attributo chiave, quindi reggiungi a una FilterExpression:

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

Ecco la trappola. DynamoDB legge ogni Item nella partizione kitchen, conta la capacità per tutti loro, e poi scarta quelli esauriti.

La regola ufficiale: una filter expression è "applicata dopo che una Query termina, ma prima che i risultati siano restituiti", e "non consuma alcuna read capacity unit aggiuntiva" — hai già pagato per la lettura completa (AWS — Filter expressions for Query).

Quindi se kitchen ha 10.000 prodotti e 12 sono in stock, paghi per leggerne 10.000. La risposta è piccola; il conto no. FilterExpression riduce il payload che attraversa il filo, mai la lettura.

C'è un secondo bordo più tagliente: la paginazione è contata prima del filtraggio. Una pagina è 1 MB di Item letti, non 1 MB di match.

Un filtro può restituire una pagina vuota con un LastEvaluatedKey impostato — DynamoDB ha letto un megabyte intero, non ha abbinato nulla, ti ha consegnato un array vuoto. Continui a paginare, e hai pagato per ogni pagina vuota.

Costruisci l'espressione — nomi, valori e il corretto escaping delle reserved word — con il DynamoDB Expression Builder così i placeholder #inStock/:val sono corretti al primo tentativo.

Confronta i quattro

Quando filtraTaglia il costo di lettura?Potenza del predicatoCosto di setup
Chiave di partizionePrima della letturaSì — una partizioneSolo uguaglianzaGratis (è la chiave)
Sort keyPrima della letturaSì — una fettaRange / begins_withDesign della sort key
Sparse indexPrima della letturaSì — solo indicePresenza di un attributoGSI extra + costo di scrittura
FilterExpressionDopo la letturaNoQuasi ogni condizioneNessuno

Leggi la tabella dall'alto in basso: la potenza del predicato sale, il controllo del costo scende. FilterExpression può esprimere qualsiasi cosa con precisione proprio perché gira su Item già letti — è la stessa ragione per cui non può farti risparmiare denaro.

Vedilo in DynoTable

Quando esegui una Query con un filtro, il divario tra Item letti e Item restituiti è tutta la storia. DynoTable mostra la capacità consumata accanto al conteggio dei risultati — così un filtro che legge silenziosamente l'intera partizione è visibile, non nascosto nel tuo conto mensile.

Per domande genuinamente cross-Item a cui un filtro non può rispondere — "prezzo medio per reparto", "prodotti in stock joinati alle loro recensioni" — il SQL Workbench di DynoTable esegue GROUP BY, JOIN e aggregati client-side su un result set limitato, invece di compilare in uno Scan sull'intera tabella.

Errori e prossimi passi

  • Non usare FilterExpression come percorso di accesso primario. Se un pattern è comune, modellalo in una chiave o uno sparse index. Un filtro serve per l'ultimo pezzetto di restringimento, non per il grosso.
  • Tieni d'occhio le pagine vuote. Una query filtrata può paginare a lungo restituendo nulla. Onora LastEvaluatedKey; non dare per scontato che una pagina vuota significhi "finito".
  • Uno sparse index non è gratis. Costa write capacity e storage per ogni Item che vi finisce — economico quando l'attributo è raro, meno quando non lo è.

Stima quanto costerà davvero una lettura filtrata con il capacity calculator, e prova DynoTable per osservare la capacità consumata rispetto alle righe restituite sulle tue tabelle.

Aggiornato