Intermedio9 min di lettura

DynamoDB GROUP BY: Come aggregare invece

Non c'è "GROUP BY" in DynamoDB. Non esiste "COUNT", "SUM" o "AVG". neanche - non nel nativo API e non in PartiQL. DynamoDB è un valore-chiave / archivio documenti, non un motore di analisi, quindi l'aggregazione è qualcosa che tu build, non qualcosa che il pianificatore di query fa per te.

Puoi GROUP BY in DynamoDB?

No. DynamoDB non ha GROUP BY, HAVING o funzioni aggregate come COUNT, SUM e AVG — né nel nativo API né in PartiQL, il cui SELECT accetta solo WHERE e ORDER BY. L'aggregazione avviene precalcolando i totali man mano che i dati cambiano (contatori atomici o+ Lambda rollup) o raggruppando lato app dopo la lettura.

  • La grammatica di DynamoDB PartiQL SELECT è SELECT … FROM … [WHERE …] [ORDER BY …]
  • Perché DynamoDB "non supporta nativamente operazioni di aggregazione come SUM o "COUNT" tra gli elementi," la guida di AWS consiste nel precalcolare gli aggregati man mano che i dati cambiano e memorizzano i risultati come elementi ordinari (AWS: aggregazione materializzata).
  • L'alternativa - leggere ogni elemento e poi aggregarlo nella tua app - funziona, ma tu pagare per leggere l'intera tabella su ogni query.
  • Per l'esplorazione una tantum, il SQL Workbench di DynoTable esegue GROUP BY / COUNT / SUM / AVG direttamente rispetto a una tabella live: SQL DynamoDB L'endpoint PartiQL rifiuta.

Perché l'aggregazione è difficile in DynamoDB

DynamoDB non ha un motore di aggregazione del tempo di scansione. Query e Scan restituiscono elementi; non li piegano. Un Scan legge l'intera tabella 1 MB alla volta e il file la capacità che consuma si basa sugli elementi che legge, non sulle righe che conservi: a FilterExpression viene applicato dopo la scansione ma prima che vengano restituiti i risultati, quindi è così restringe il set di risultati senza abbassare il conto (AWS Scan API riferimento: un filtro "non consuma unità di capacità di lettura aggiuntive"; la capacità è basata sull'articolo dimensione scansionata, non restituita). Non c'è nessun hook GROUP-BY appendere una somma o su cui contare in primo luogo.

PartiQL non cambia questo. PartiQL è un dialetto SQL-compatibile sullo stesso motore, quindi eredita gli stessi limiti: è una superficie sintattica, non una nuova modello di esecuzione. La grammatica SELECT documentata semplicemente non ha alcun token "GROUP BY". Per il divario completo tra PartiQL e SQL reale, vedere PartiQL vs SQL.

Chiedi dove vive il tuo aggregato e quando viene calcolato. Ci sono tre risposte.

Modello 1: aggregazione in scrittura (contatori atomici)

Se conosci i gruppi in anticipo: conta per stato, totale per cliente, download al mese: conserva un contatore e aggiornalo a ogni scrittura.

Utilizza un "AGGIUNGI".quindi l'incremento è atomico e sicuro per la concorrenza. ADD funziona su numeri e insiemi ed evita la corsa di lettura-modifica-scrittura, quindi due scrittori che incrementano lo stesso contatore non si ostacolano mai a vicenda (AWS rileva l'atomico ADD "evita le condizioni di competizione di lettura-modifica-scrittura"):

UpdateItem
Key                         { pk: "STATS#orders", sk: "status#shipped" }
UpdateExpression            "ADD orderCount :one"
ExpressionAttributeValues   { ":one": 1 }

Questo è il tuo stato SELECT COUNT(*) … GROUP BY, tranne per il fatto che il conteggio è già presente seduto lì come un elemento, leggibile in un millisecondo GetItem di una sola cifra. Il compromesso: devi conoscere la chiave di raggruppamento al momento della scrittura e accoppiare il file contatore aggiornamento al percorso di scrittura. Se l'app si arresta in modo anomalo dopo la scrittura ma prima dell'aggiornamento del contatore, i due non sono più sincronizzati, il che è esattamente il problema modalità di errore il modello successivo si disaccoppia.

Modello 2: DynamoDB Stream + Lambda rollup

Quando non vuoi la logica di aggregazione sul percorso di scrittura o la scrittura è a semplice PutItem che non puoi avvolgere facilmente: spostalo a valle. Questo è proprio di AWS modello consigliato, aggregazione materializzata (AWS: utilizzo di GSIs per query di aggregazione materializzate):

  1. L'app scrive l'elemento grezzo (un ordine, un download, un evento). Nessuna aggregazione logica. 2.acquisisce la scrittura come record di flusso.
  2. Un Lambda collegato allo stream legge il nuovo elemento e deriva il gruppo (stato, mese, categoria...) e "AGGIUNGI" all'elemento aggregato corrispondente con un atomico UpdateItem — che "evita condizioni di competizione di lettura-modifica-scrittura" quando molte invocazioni toccano lo stesso contatore.
  3. Interroga l'aggregato precalcolato, spesso tramite un quello indicizza solo gli elementi cumulativi, quindi "top 10 questo mese" è un Query con "Limite 10".

Solo gli elementi aggregati portano l'attributo indicizzato (ad esempio "Mese"), quindi le righe degli eventi non elaborati vengono escluse automaticamente dall'indice — "una piccola frazione del totale degli elementi nella tabella", che mantiene l'indice economico e la lettura veloce.

Ciò disaccoppia l'aggregazione dal percorso di scrittura e mantiene le scritture semplici, allo stesso tempo costo di eventuale coerenza — AWS nota "un ritardo di pochi secondi tra un download in corso di registrazione e aggregazione in corso di aggiornamento." Per i cruscotti, classifiche e contatori di tendenza va bene.

Un nuovo tentativo di invocazione Lambda esegue nuovamente "ADD", quindi "un nuovo tentativo incrementerebbe il conteggio più di una volta", lasciando un valore approssimativo valore. Per conteggi esatti, aggiungi l'idempotenza (ad esempio un'espressione di condizione digitata su l'ID dell'elemento di origine); altrimenti il piccolo margine va bene per l'analisi e classifiche.

Modello 3: raggruppamento lato app dopo Scan/Query

Oppure leggi gli elementi e raggruppali nel tuo codice.

groups = {}
resp = table.scan()                        # or query() for one partition
while True:
    for item in resp["Items"]:
        key = item["status"]
        groups[key] = groups.get(key, 0) + 1
    if "LastEvaluatedKey" not in resp:
        break
    resp = table.scan(ExclusiveStartKey=resp["LastEvaluatedKey"])

Questo è corretto e talvolta è la scelta giusta, ma sii onesto riguardo al costo. A Scan legge ogni elemento nella tabella e la capacità di lettura è la stessa se filtri o meno. Quindi il raggruppamento lato app su un Scan completo significa che paghi per leggere l'intera tabella su ogni aggregazione e la latenza aumenta con la tabella. AWS elenca "scansione e conteggio al momento della lettura" come "adatto solo per molto piccoli set di dati in cui la latenza non è un problema" (AWS: Perché pre-calcolare le aggregazioni).

Limitato a una singola partizione tramite Query (ad esempio contare gli ordini per uno cliente), il raggruppamento lato app è perfettamente ragionevole: ne stai leggendo solo uno raccolta di oggetti. Per il divario di costo completo tra i due, vedere Query vs Scan. In "us-east-1" on-demand, una tabella completa Scan fattura 0,5 RCU per 4 KB alla fine coerenti per ogni elemento esaminato — una tabella da 1 GB di righe da 1 KB è di circa 250.000 RCU prima dell'app raggruppa qualsiasi cosa. Taglia un articolo rappresentativo con il calcolatore della dimensione dell'oggetto, quindi valuta la linea scansiona nel calcolatore dei prezzi.

Per un SQL analitico veramente ad hoc su una tabella DynamoDB: l'usa e getta "GROUP BY stato, contali" eseguiresti una volta — la risposta di AWS è puntare a motore separato: il connettore Amazon Athena DynamoDB ti consente di eseguire query la tabella con SQL reale (GROUP BY, aggregati, anche JOIN ad altre fonti) tramite un connettore Lambda (AWS: connettore Amazon Athena DynamoDB). Esegue la scansione della tabella dietro le quinte, quindi è uno strumento di reporting/BI, non un percorso caldo.

Quale modello utilizzo?

Hai bisogno di…Utilizzare
Un totale di gruppo noto su un percorso di lettura attivoModello 1 — contatore atomico (ADD)
Aggrega senza toccare il percorso di scritturaModello 2: flussi + rollup Lambda
Un conteggio limitato a una partizioneModello 3 — Query quindi raggruppa nell'app
Totali esatti, nessuna derivaModello 1/2 con protezione idempotency
Un GROUP BY unico durante l'esplorazioneDynoTable Workbench (sotto) o Atena
BI/reporting ricorrente con SQLConnettore Athena DynamoDB

Esecuzione di GROUP BY direttamente nel SQL Workbench di DynoTable

I modelli sopra riportati rappresentano il modo in cui servi gli aggregati in produzione. Ma quando sei esplorando una tabella: "quanti ordini per stato, in questo momento?" - non vuoi fornire una Lambda o alzare Atena. Vuoi digitare la query.

Ecco a cosa serve il SQL Workbench di DynoTable. Esegue SQL reale — "GROUP BY", "COUNT", "SUM", "AVG", "HAVING", anche "JOIN" — direttamente contro il tuo live DynamoDB tabelle, eseguendo l'aggregazione lato client sulle righe legge. È il SQL che l'endpoint PartiQL di DynamoDB rifiuta:

SELECT status, COUNT(*) AS orders, SUM(total) AS revenue
FROM "Orders"
GROUP BY status
HAVING SUM(total) > 1000
ORDER BY revenue DESC

Sotto il cofano DynoTable legge gli elementi come consente API (Query dove può, Scan dove deve), li materializza, e fa il raggruppamento in Workbench - la stessa meccanica "leggi quindi aggrega" di Pattern 3, semplicemente senza il loop e entro le regole del modello di accesso di DynamoDB. Lo è costruito per esplorazione e analisi ad hoc, non per sostituire una produzione rollup su un percorso di lettura attivo. Per questo, precalcola (modello 1/2).

Per il lato "JOIN" dello stesso cuneo: DynoTable esegue join tra tabelle PartiQL non puoi neanche - vedi DynamoDB JOIN. Confronto dell'interfaccia utente grafica clienti esattamente su questa funzionalità? Vedi il confronto della GUI DynamoDB.

FAQ

DynamoDB PartiQL supporta GROUP BY? No. DynamoDB PartiQL SELECT supporta solo WHERE e ORDER BY — no "GROUP BY", "HAVING", funzioni aggregate o "JOIN". La grammatica è documentato come `SELEZIONA... DA... [DOVE...] [ORDER BY...]".

Posso eseguire COUNT(*) su un'intera tabella DynamoDB? Non come funzione aggregata: PartiQL non ne ha. Il API ti dà Select=COUNT su un Scan/Query, che restituisce un conteggio di elementi corrispondenti ma continua a leggere (e fatturare) ogni elemento toccato dalla scansione (riferimento AWS Scan API: la capacità si basa sugli articoli esaminati, non restituiti). Per una lettura frequente totale, mantieni un contatore (schema 1).

Posso GROUP BY la chiave di partizione? Non in DynamoDB o PartiQL. Se "per chiave di partizione" è un modello di accesso noto, mantieni un elemento aggregato per chiave con un "ADD" atomico (modello 1) o lancialo con Streams + Lambda (schema 2).

Come faccio a eseguire "SUM" o "AVG" per gruppo? "SOMMA": mantiene un totale parziale per gruppo e lo aggiunge in scrittura. "AVG": memorizza sia la somma che il conteggio e la divisione al momento della lettura: non esiste una media nativa. Per un AVG esplorativo una tantum, eseguilo nel SQL Workbench di DynoTable o tramite Connettore Athena DynamoDB.

Esiste una soluzione alternativa partiql group by? No PartiQL lato uno. Precalcolare l'aggregato (contatori/flussi) e "SELEZIONA" l'elemento di rollup o esegui "GROUP BY" in un motore che ne ha uno: DynoTable's Workbench per ad-hoc, Athena per reporting ricorrente.


Vuoi eseguire "GROUP BY" sulle tue tabelle senza scrivere un Lambda? Prova DynoTable e punta il SQL Workbench su una tabella live.

Aggiornato