Principiante6 min di lettura

Query vs Scan in DynamoDB

Query legge una singola collezione di Item per partition key (restringendo facoltativamente la sort key); Scan legge l'intera tabella e filtra in seguito. Sembrano simili nell'API ma fatturano — e scalano — in modo completamente diverso.

Quando dovrei usare Query vs Scan in DynamoDB?

Usa Query ogni volta che puoi indicare la partizione di cui hai bisogno — legge una sola e fattura solo gli Item corrispondenti. Ricorri a Scan solo per export una tantum o tabelle piccole; legge ogni Item e fattura l'intera tabella prima che venga eseguita qualsiasi FilterExpression. Con dati reali, Query vince.

  • Query è mirata: paghi per gli Item nella partizione corrispondente.
  • Scan è esaustiva: paghi per leggere ogni Item, poi ne scarti la maggior parte con una FilterExpression che gira dopo che la lettura è stata conteggiata.

Su una tabella di dimensioni reali, uno Scan con un filtro è il classico trabocchetto del "perché la mia bolletta è enorme e la mia latenza è peggio di RDS".

Affiancati

QueryScan
LettureUna partizione (per PK)Ogni Item della tabella
Capacità fatturataItem corrispondenti nella partizioneIntera tabella, prima del filtro
FilterExpressionApplicata dopo la lettura — comunque fatturata per la letturaIdem — il filtro non riduce mai il costo
LatenzaCostante al crescere della tabellaCresce con la dimensione della tabella
Paginazione1 MB/pagina → LastEvaluatedKey1 MB/pagina; parallelizzabile
Usala perPattern di accesso notiExport una tantum, piccole tabelle di config

La trappola chiave: una FilterExpression gira dopo che DynamoDB ha conteggiato la lettura, su entrambe le operazioni. Uno Scan che "restituisce 10 righe" può fatturare la lettura di un milione — il filtro è una comodità, mai un controllo dei costi.

Quanto costa davvero uno Scan completo

Mettiamoci dei numeri. DynamoDB contabilizza le letture in unità da 4 KB: una lettura costa una ogni 4 KB, una lettura la metà. Query e Scan sommano la dimensione di ogni Item che toccano — non di ogni Item che restituiscono — e arrotondano per eccesso ai successivi 4 KB.

Prendi una tabella da 1 milione di Item con una media di 2 KB per Item (~2 GB di dati) e un pattern di accesso che ha bisogno di 10 di quegli Item:

Item lettiDati contabilizzatiUnità di lettura (eventualmente coerenti)
Scan + FilterExpression1.000.000~2 GB~262.000
Query su una chiave corrispondente1020 KB3

Gli stessi 10 Item, cinque ordini di grandezza di distanza — e lo Scan fattura quelle ~262.000 a ogni esecuzione, che il filtro trovi dieci Item o nessuno. Con la fatturazione sono request unit che finiscono dritte in bolletta; sulle tabelle un grande Scan compete con il traffico di produzione per il throughput e può strozzarlo fino a una ProvisionedThroughputExceededException.

Altri tre fatti sui costi che sorprendono:

  • Select: COUNT non è gratis. Una Query o uno Scan di conteggio consuma esattamente la stessa capacità di lettura della lettura degli Item — semplicemente non li restituisce.
  • Limit limita gli Item valutati, non gli Item corrispondenti. Combinato con un filtro, una pagina può tornare vuota fatturando comunque una pagina intera di letture.
  • Non devi mai tirare a indovinare. Passa ReturnConsumedCapacity: TOTAL e ogni risposta riporta la capacità appena consumata.

Verifica quanto pesano i tuoi Item con il calcolatore della dimensione degli Item, poi trasforma le unità di lettura in una bolletta mensile con il calcolatore di prezzi.

Usa Query

Query  PK = "USER#42"  AND  SK begins_with "ORDER#"

Se ti ritrovi a ricorrere a Scan per rispondere a un pattern di accesso comune, è un segnale di modellazione: aggiungi un Global Secondary Index così il pattern diventa una Query.

La scelta si riduce a una domanda — sai indicare la partizione che ti serve?

YesNoYesNoAccess patternPartition key known?Query reads one partitionCan a GSI key it?Add a GSIScan reads the whole table

Se conosci la chiave fai una Query; altrimenti aggiungi un GSI per renderla tale, e ricorri allo Scan solo quando nessuna chiave è adatta.

Quando Scan va bene

Export una tantum, piccole tabelle di config e job in background che paginano deliberatamente l'intera tabella. Usa Segment/TotalSegments per suddividere uno Scan tra più worker (uno — vedi gli Scan paralleli in DynamoDB) quando devi davvero leggere tutto, e paginalo correttamente con LastEvaluatedKey (guida alla paginazione). Se il problema è uno Scan che esegui già, perché Scan è lento e costoso ti guida nel triage.

Un riflessivo SELECT * FROM table su DynamoDB è lo stesso anti-pattern travestito da PartiQL — si compila in uno Scan. Quando hai davvero bisogno di analisi tra più Item (un GROUP BY, un JOIN, un aggregato), il Workbench SQL di DynoTable li esegue lato client su un insieme di risultati limitato invece di martellare la tabella.

Prova DynoTable per eseguire e ispezionare queste query sulle tue tabelle — mostra la capacità consumata di ogni operazione che esegue.

Aggiornato