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).
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 filtra | Taglia il costo di lettura? | Potenza del predicato | Costo di setup | |
|---|---|---|---|---|
| Chiave di partizione | Prima della lettura | Sì — una partizione | Solo uguaglianza | Gratis (è la chiave) |
| Sort key | Prima della lettura | Sì — una fetta | Range / begins_with | Design della sort key |
| Sparse index | Prima della lettura | Sì — solo indice | Presenza di un attributo | GSI extra + costo di scrittura |
| FilterExpression | Dopo la lettura | No | Quasi ogni condizione | Nessuno |
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
FilterExpressioncome 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.