Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

DynamoDB: cosa sbaglia la maggior parte dei team prima ancora di scrivere una riga di codice

Questa pagina è disponibile anche in English, Deutsch, Español, Français, 日本語 e Português.

By Joseph AllamJul 20, 20268 min read
Joseph Allam

About Joseph Allam

Databases have been my craft for a long time — long before the cloud, and deep into it across AWS and GCP. I know what breaks, what costs too much, and what needs rethinking before it hurts. I also know how to bring AI into the work: into how databases are designed, operated, and accessed by the intelligent systems being built on top of them.

My personal page

Durante il Prime Day 2025, Amazon DynamoDB ha raggiunto un picco di 151 milioni di richieste al secondo. È il database alla base di alcuni dei workloads più esigenti del pianeta, e lo è da quasi vent'anni. La maggior parte degli engineer che lavora su AWS ne ha sentito parlare. Molti lo hanno usato. Ma durante il DynamoDB Immersion Day che ho tenuto di recente insieme al team AWS, lo stesso problema è emerso in quasi tutte le sessioni: team che avevano scelto DynamoDB per le ragioni giuste continuavano ad avere difficoltà perché progettavano gli schemi nel modo sbagliato.

Questo articolo parla proprio di questo. Non delle basi di DynamoDB (di quelle si trova materiale in abbondanza altrove), ma dell'approccio progettuale che distingue i team che lo fanno funzionare bene da quelli che finiscono con costi fuori controllo e tabelle che non scalano.

PerfectScale™ for Kubernetes

Ready to optimize?

Get your free Kubernetes savings analysis

DynamoDB è la scelta giusta?

Prima di entrare nella progettazione, conviene essere chiari su quando DynamoDB è davvero adatto. È una buona scelta quando serve una latenza costante di pochi millisecondi praticamente a qualsiasi scala, i pattern di accesso sono noti e prevedibili fin dall'inizio e il workload è dominato da letture e scritture, più che da analisi complesse. Buoni esempi: API rivolte agli utenti, session store, classifiche, carrelli e-commerce, ingestion di dati IoT, event sourcing.

È invece una scelta sbagliata quando servono query ad hoc su molte dimensioni, i pattern di accesso sono ancora in evoluzione o si ha a che fare con join relazionali complessi. Se si sta sostituendo un database di reporting o costruendo qualcosa in cui i requisiti delle query cambieranno di mese in mese, si finirà per combattere contro DynamoDB a ogni passo.

La dimensione degli item conta più di quanto la maggior parte dei team si aspetti

Prima di puntare su DynamoDB, è bene modellare i costi con attenzione. DynamoDB addebita la capacità di scrittura in incrementi da 1 KB, arrotondati per eccesso. In modalità on-demand un item da 20 KB consuma 20 Write Request Unit (WRU); in modalità provisioned la stessa scrittura consuma 20 Write Capacity Unit (WCU). Le letture funzionano in modo analogo, fatturate in incrementi da 4 KB per Read Capacity Unit (RCU): la dimensione degli item incide quindi su entrambi i lati del costo di capacità.

Sembra semplice, ma la dimensione degli item è una delle variabili più spesso trascurate quando i team modellano i costi di DynamoDB. Vediamo regolarmente clienti che migrano da database relazionali come PostgreSQL, dove il concetto di fatturazione per byte sulle scritture non esiste, e le loro stime di costo iniziali risultano decisamente sbagliate perché non hanno considerato correttamente la dimensione degli item. Calcolare bene quel numero prima di impegnarsi cambia completamente l'economia del progetto.

Il punto non è che DynamoDB sia costoso. Il punto è che il modello di costo è diverso da quello a cui la maggior parte degli engineer è abituata, e le variabili che lo determinano (dimensione degli item, efficienza dei pattern di accesso, capacità on-demand vs provisioned) vanno comprese prima di impegnarsi, non dopo.

Il nuovo modello mentale che cambia tutto

Ecco l'abitudine che la maggior parte degli engineer porta su DynamoDB da un background relazionale: prima si progetta lo schema, poi si pensa alle query.

Con un database relazionale, questo approccio funziona quasi sempre. Si normalizzano i dati, si costruiscono gli indici in un secondo momento, si scrivono query con join tra tabelle. Il motore del database gestisce gran parte della complessità delle query.

DynamoDB non funziona così. A differenza dei database relazionali, non ottimizza query arbitrarie tramite un query planner. Instrada invece ogni richiesta direttamente alla partizione che detiene i dati usando l'hash della partition key, uno dei motivi per cui DynamoDB riesce a garantire in modo costante latenze di pochi millisecondi indipendentemente dalla dimensione della tabella. Il compromesso è che il database è ottimizzato per una cosa sola: recuperare item in modo efficiente tramite pattern di accesso basati su chiavi, attraverso la primary key o gli indici secondari. Tutto il resto (filtrare, ordinare per attributi non chiave, interrogare relazioni tra entità) è costoso oppure richiede indici aggiuntivi progettati in anticipo.

Il cambio di prospettiva è questo: bisogna conoscere i propri pattern di accesso prima di progettare la tabella. Non a grandi linee. Nel dettaglio. "Recuperare tutti gli ordini di un utente, ordinati per data" è un input di progettazione. "Recuperare i 10 ticket di supporto in escalation più recenti per un determinato account" è un input di progettazione. Lo schema si costruisce a ritroso a partire da quelle domande, non dalla forma dei dati.

In pratica questo significa che una singola tabella DynamoDB spesso ospita più tipi di entità fianco a fianco, con partition key e sort key costruite per rispondere in modo efficiente a domande specifiche. A chiunque provenga da un background SQL normalizzato sembrerà un errore. Non lo è. È proprio questo il punto.

Sort key composite

Prendiamo un esempio comune: gli ordini di un utente, con la necessità di interrogare per stato e data. L'approccio ingenuo memorizza stato e data come attributi separati e poi filtra in fase di lettura. Il risultato è che si leggono tutti gli ordini di quell'utente scartando quelli che non corrispondono.

L'approccio che parte dai pattern di accesso codifica la query direttamente nella sort key:

PK SK
user#123 ACTIVE#2024-07-15
user#123 ACTIVE#2024-07-01
user#123 COMPLETED#2024-06-20
user#123 COMPLETED#2024-06-10

Ora una singola query con BEGINS_WITH(\"ACTIVE#\") restituisce solo gli ordini attivi, ordinati per data, senza leggere nemmeno un ordine completato. Si paga esattamente per ciò che si recupera.

Questo funziona perché BEGINS_WITH è una query per prefisso sulla sort key. Il raggruppamento più ampio deve venire per primo, in modo che DynamoDB possa usarlo per restringere l'intervallo. Un approccio comune consiste nel collocare la condizione più ampia a sinistra e componenti via via più specifici a destra, così che le query per prefisso possano puntare in modo efficiente al sottoinsieme di interesse.

Global Secondary Index sparsi

Un altro pattern che deriva dal ragionare prima sui pattern di accesso: se solo una piccola frazione degli item soddisfa una determinata condizione (ticket in escalation, record segnalati, elementi in attesa di revisione), è possibile modellare quella condizione come un attributo presente solo su quegli item. Un GSI costruito su quell'attributo indicizza soltanto gli item in cui l'attributo esiste.

Il comportamento chiave è che DynamoDB non include un item in un GSI quando l'attributo indicizzato è assente (non semplicemente null, ma del tutto mancante dall'item). Questo significa che si può rendere un GSI sparso per costruzione, semplicemente omettendo l'attributo sugli item che non si vogliono indicizzare.

Se il 2% dei ticket è in escalation, il GSI contiene il 2% della tabella. Interrogarlo è enormemente più economico che scansionare l'intera tabella e filtrare. Il GSI è sparso per scelta progettuale, non per caso.

Your cloud bill shouldn't be a mystery

Optimization, automation, expertise. In one platform.

Gli errori che si vedono in fattura

Vale la pena citarli uno per uno perché sono comuni, restano invisibili finché non si guardano i report dei costi e di solito vengono introdotti da engineer che conoscono DynamoDB ragionevolmente bene.

Filter expression

DynamoDB supporta il filtraggio dei risultati delle query dopo il recupero. Il problema è che una FilterExpression non riduce le unità di capacità consumate; riduce soltanto ciò che viene restituito al chiamante. Se una query legge 500 item prima che il filtro venga eseguito, si paga per tutti i dati letti fino a quel punto, indipendentemente da quanti item vengono restituiti al chiamante.

Questo diventa costoso in fretta quando è la strategia principale per restringere i risultati. Assomiglia a una clausola WHERE di SQL, quindi è facile ricorrervi senza pensare al costo. La soluzione è quasi sempre riprogettare la struttura delle chiavi in modo che il database recuperi solo ciò che serve. Le filter expression hanno la loro utilità, ma non dovrebbero essere il pilastro portante della strategia di query.

Hot partition causate da chiavi temporali

Un pattern comune quando si gestiscono dati time-series: usare il timestamp corrente o l'ora corrente come partition key. Il ragionamento sembra intuitivo: partizionare per intervallo temporale. Il problema è che tutte le scritture puntano alla stessa partition key logica, impedendo a DynamoDB di distribuire il traffico di scrittura in modo uniforme tra le partizioni. A qualsiasi volume di scrittura significativo questo porta al throttling.

DynamoDB dispone dell'Adaptive Capacity, che ridistribuisce automaticamente il throughput verso le partizioni che ricevono più traffico. In caso di squilibri moderati, spesso gestisce il problema in modo trasparente. Ma nessuna capacità adattiva può compensare una singola partition key che riceve traffico estremo e prolungato, e una chiave basata su intervalli temporali sotto un carico di scrittura elevato è esattamente quella situazione.

La soluzione è il write sharding: aggiungere un suffisso casuale alla partition key (ad esempio EVENTS#4, dove il suffisso è un numero tra 0 e N) per distribuire le scritture su più partizioni logiche. In fase di lettura, si interroga ogni shard in parallelo e si uniscono i risultati. Richiede più codice applicativo, ma è ciò che impedisce a un workload di scrittura ad alto throughput di creare un collo di bottiglia su una singola partizione.

Scan di tabella

Il terzo errore da segnalare: le scansioni complete della tabella. DynamoDB supporta le operazioni di Scan, e gli engineer a volte vi ricorrono quando esauriscono i GSI o devono interrogare per un attributo non indicizzato. Il problema è che una scan legge ogni item della tabella, indipendentemente da quanti corrispondono. Su una tabella di grandi dimensioni, questo è al tempo stesso lento e costoso.

Le scan sono perfettamente ragionevoli per migrazioni, export, job amministrativi o elaborazioni in background dove la latenza non conta. Il problema è usarle nei flussi di richiesta rivolti agli utenti. Se una scan si trova nel flusso critico dell'applicazione, è quasi sempre il segnale che i pattern di accesso non sono stati considerati appieno nella progettazione dello schema. La soluzione è di solito un nuovo GSI o una sort key riprogettata, non una scan più veloce.

Considerazioni finali

DynamoDB è davvero potente e, per i workloads giusti, è uno dei migliori database offerti da AWS. Ma il divario tra "aver scelto il database giusto" e "averlo progettato correttamente" è ampio, e quel divario si manifesta nella fattura AWS prima che altrove. Il modello di costo è diverso, il processo di progettazione è diverso e gli errori sono diversi da quelli in cui la maggior parte degli engineer si è imbattuta finora.

Se sta valutando DynamoDB per un nuovo workload, o ha già tabelle in produzione con costi più difficili da spiegare di quanto vorrebbe, questo è esattamente il tipo di lavoro che DoiT svolge ogni settimana con i clienti AWS. Il nostro team di oltre 100 esperti cloud può aiutarla a validare i pattern di accesso, individuare tempestivamente i problemi di progettazione e ottenere il massimo dai servizi per cui sta già pagando. Richieda una demo.