Benvenuto nel mondo di DynamoDB, dove efficienza e scalabilità illimitata convergono in un'unica tabella. In questo tutorial completo, intraprenderemo un percorso stimolante per svelare il vero potenziale del design di database NoSQL a tabella singola.
Scopri l'arte di semplificare modelli di dati complessi sfruttando tutta la potenza di una struttura unificata, in grado di accogliere senza sforzo le tue esigenze in continua crescita.
Che tu sia uno sviluppatore esperto di database NoSQL o che tu stia appena iniziando il tuo percorso nel mondo dei database, preparati a sbloccare un nuovo livello di scalabilità per le tue applicazioni con una potente architettura dati!
Che cos'è il NoSQL?
Prima di iniziare, è importante capire la differenza tra NoSQL e database relazionali, in quali scenari useremmo ciascuno e perché queste tecnologie sono state inventate. Questo richiede una breve occhiata alla storia dell'elaborazione dei dati.
Il database relazionale è una tecnologia ben nota e ampiamente utilizzata sin dagli anni '70. Due dei principali problemi che il database relazionale risolve sono l'archiviazione e l'integrità referenziale.
I dati sono "normalizzati", tipicamente alla terza forma normale, il che riduce l'impronta dei dati e garantisce che eventuali aggiornamenti restino coerenti con il resto dei dati.
La dimensione dell'impronta dei dati persistenti era cruciale perché, storicamente, il componente più costoso nel data center era il disco rigido. Oggi non è più così: la CPU è diventata la componente più costosa e l'archiviazione dei dati si è drasticamente ridotta di costo. Questo non significa che il database relazionale sia obsoleto; ha applicazioni valide per:
Applicazioni in cui i pattern di accesso non sono ben definiti
CMS / headless CMS
Database NoSQL
La pressione sui dati è la capacità di un sistema di elaborare una determinata quantità di dati a costi e/o tempi ragionevoli.
I database NoSQL sono stati inventati alla fine degli anni '90 per affrontare i problemi di pressione dei dati che influivano sulle prestazioni dei database relazionali a causa dei volumi sempre crescenti di dati applicativi.
La CPU in un database relazionale può essere considerata uno dei principali colli di bottiglia, poiché il server relazionale si affanna a recuperare e ricomporre dati normalizzati nelle viste denormalizzate che le applicazioni in genere consumano.
Questo può essere alleviato aggiungendo server replica di sola lettura, ma NoSQL adotta un approccio diverso e memorizza i dati applicativi in forma denormalizzata, pronta per essere consumata dalle applicazioni. Ciò significa che l'impronta dei dati è maggiore rispetto a un database relazionale, ma alleggerisce il carico sulla CPU, rendendo di fatto il server del database un semplice router che usa un algoritmo di hash per puntare a una posizione su un disco nel data center. Gli utilizzi tipici dei database NoSQL sono:
Applicazioni che devono scalare orizzontalmente per supportare grandi volumi di domanda a livello regionale o globale
Numerosi studi ci hanno fornito abbastanza dati oggettivi sull'argomento per presentare un grafico che rappresenta le prestazioni di SQL vs NoSQL su larga scala.
L'ultimo punto da chiarire è che i dati archiviati in un database NoSQL sono comunque dati relazionali. Se i dati non fossero relazionali, li archivieremmo in un semplice filestore.
Antipattern NoSQL
Non modelliamo un database NoSQL nello stesso modo in cui modelliamo un database relazionale, quindi è un antipattern tradurre un design normalizzato direttamente in un database NoSQL usando più tabelle collegate tra loro, perché NoSQL non ha l'operatore di join né l'integrità referenziale.
Una tabella in NoSQL è l'equivalente di un catalogo in un database relazionale. Invece, modella i dati per essere memorizzati in forma denormalizzata, pronti per il consumo da parte delle applicazioni.
Un altro antipattern sono le Hot Key. Significa che la maggior parte, se non tutte, le richieste di accesso colpiscono un singolo nodo di archiviazione, il che implica che lo spazio delle chiavi non è stato progettato correttamente.
La heatmap degli accessi apparirà più o meno così:
DynamoDB
AWS DynamoDB è un archivio key-value a colonne larghe, completamente gestito e serverless, il che significa che supporta molti item, o righe, in una tabella, ma questi item non hanno necessariamente gli stessi attributi. I tipi di dato supportati sono i seguenti:
Tipi scalari: stringa, numero, binario, booleano, null
Tipi documento: struttura complessa con attributi annidati (cioè JSON)
Tipi set: set di stringhe, set di numeri, set binari
Come detto in precedenza, NoSQL andrebbe usato quando i pattern di accesso dell'applicazione sono ben noti e c'è bisogno di supportare un elevato numero di TPS. DynamoDB può scalare per qualsiasi carico di lavoro e garantisce tempi di risposta prevedibilmente rapidi e costanti fino a 4 milioni di TPS con bassa latenza (10 - 20 ms).
Come funziona DynamoDB?
Ogni item (o riga) ha una chiave di partizione che lo identifica in modo univoco e determina la distribuzione dei dati sull'archiviazione sottostante.
Gli item possono avere facoltativamente una chiave di ordinamento, che determina l'ordine in cui i dati sono memorizzati su disco all'interno di una partizione. Le chiavi di ordinamento ci consentono di interrogare le partizioni con operazioni complesse di range e filtro (anche se i filtri sono applicati a livello di client, non di database).
La chiave di partizione viene usata per creare un indice hash e poi ogni item viene disposto secondo questo hash su uno spazio chiavi virtuale. Questo spazio chiavi virtuale è diviso in segmenti che mappano su dispositivi di archiviazione fisici, che possono crescere e ridursi dinamicamente a seconda del volume di dati memorizzati.
Ecco perché DynamoDB è veloce e coerente a qualsiasi scala: perché il motore del database agisce come un servizio di instradamento dalle chiavi con hash allo storage fisico.
Data modeling con NoSQL
In questo tutorial modelleremo un negozio online chiamato Daintree.com.
(Curiosità: la foresta pluviale di Daintree, nel Queensland, Australia, è una delle più antiche al mondo, con un'età stimata di circa 180 milioni di anni!)
Per prima cosa, creiamo un diagramma ERD (entity relationship diagram) molto semplificato per visualizzare ciò che andremo a modellare.
Da questo, possiamo dedurre che un cliente può creare un carrello e aggiungere più prodotti con quantità variabili.
Un prodotto ha una categoria, che a sua volta può avere categorie padre.
I carrelli possono diventare ordini che possono essere suddivisi in più pagamenti.
Come già detto, questa è una visione altamente semplificata delle relazioni e degli attributi delle entità che compongono un'applicazione di shopping online, ma è sufficiente allo scopo di questo tutorial.
Definire i pattern di accesso
Non dobbiamo preoccuparci di questo passaggio quando implementiamo un database relazionale. SQL è un linguaggio incredibilmente potente che, a patto che lo schema sia stato progettato correttamente, ci consente la flessibilità di interrogare e manipolare i dati come meglio crediamo. Non è così quando si usa il pattern a tabella singola in NoSQL, perché dobbiamo archiviare i dati in forma denormalizzata e pronti per essere consumati direttamente dalla nostra applicazione.
Ecco i pattern di accesso che potremmo voler supportare per la nostra applicazione di shopping online:
Interrogare tutti i prodotti per categoria
Interrogare le sottocategorie di una categoria padre
Interrogare tutti gli ordini per cliente
Interrogare tutti i pagamenti per un ordine
Interrogare un ordine specifico per un cliente
Interrogare tutti gli articoli del carrello per il carrello di un cliente
Interrogare tutti i prodotti in base alla cronologia ordini di un cliente
Modellare la tabella
NoSQL workbench è uno strumento gratuito fornito da AWS per modellare tabelle NoSQL per DynamoDB.
Vai al Data modeler e crea un nuovo modello di dati con una nuova tabella:
Terremo gli attributi della chiave primaria come stringhe generiche PK e SK:
Per gli attributi aggiuntivi, memorizzeremo l'entity_type insieme a due attributi di chiave primaria per le GSI PK e SK.
Una GSI è un'indice secondario globale, cioè una copia della tua tabella che DynamoDB mantiene sincronizzata (in modo asincrono!!) ma archiviata usando coppie alternative di attributi di chiave primaria. Questo è il meccanismo chiave con cui una tabella singola può supportare molteplici pattern di accesso. Ne parleremo più avanti; per ora, ti basta capire che queste chiavi memorizzeranno le chiavi primarie delle entità correlate.
Gli altri attributi aggiuntivi da aggiungere sono tutti gli attributi non chiave dei nostri dati (nota che questo passaggio è necessario solo in fase di progettazione e non verrà definito esplicitamente quando effettivamente approvvigioneremo la tabella):
Successivamente, aggiungiamo le due GSI utilizzando le chiavi definite nel passaggio precedente:
Clicca su salva e poi su visualizza il modello di dati. Ora dovresti vedere la tabella appena creata ma vuota. Clicca su modifica tabella, quindi in alto a destra su modifica dati; questo ti permetterà di aggiungere nuove righe:
Prefissi dei dati
Prima di iniziare ad aggiungere dati, dobbiamo definire i prefissi della chiave primaria per ogni entità:
prodotto
p#
categoria
c#
cliente (utente)
u#
carrello
b#
articolo del carrello
bi#
ordine
o#
riga d'ordine
ol#
pagamento
py#
fattura
i#
Modellare i dati
Dati del cliente
La maggior parte dei pattern di accesso ruota attorno al cliente, quindi per il nostro indice primario useremo l'ID cliente come partizione principale, con il record effettivo del cliente identificabile grazie alla chiave di ordinamento che è anch'essa l'ID cliente.
Possiamo poi aggiungere facilmente le relazioni cliente-ordini e cliente-carrelli usando la stessa chiave di partizione ma con una chiave di ordinamento univoca.
La nostra vista aggregata ora appare così:
Ora possiamo interrogare tutti e tre i record con un unico comando, interrogando l'intera partizione di quel cliente:
Man mano che la tabella cresce, questo tipo di query potrebbe non essere il più efficiente, dato che restituirà ogni ordine del cliente mai effettuato insieme a tutti gli altri record del cliente, come carrelli e fatture, ma è utile sapere che possiamo accedere a tutti i dati di un cliente con una singola query se necessario.
Un piccolo aggiustamento ai valori dell'espressione degli attributi per includere una corrispondenza parziale (begins_with) sulla chiave di ordinamento ci consente di interrogare:
Tutti i record dei carrelli del cliente
Tutti i record degli ordini del cliente
Il punto chiave è come possiamo modellare relazioni uno-a-uno e uno-a-molti in NoSQL.
Categorie
Queste entità rappresentano un'altra relazione uno-a-molti in cui una categoria può contenere molti prodotti.
Le categorie hanno anche una relazione gerarchica in cui una categoria può avere molte sottocategorie, il che significa che i prodotti possono esistere ovunque nell'albero gerarchico delle categorie.
Modelliamo questo con i libri suddivisi in narrativa e saggistica.
Nota come abbiamo popolato gli attributi chiave della GSI per le sottocategorie, impostando GSI1_PK come l'ID della categoria padre e GSI1_SK come l'ID della sottocategoria. Questo ci permette di interrogare tutte le sottocategorie per categoria usando GSI1:
Lo stesso indice, GSI1, può essere usato non solo per mappare relazioni uno-a-molti. Poniamo per esempio di voler cercare il cliente per email; l'attuale modello non lo supporta direttamente a meno che non apportiamo una piccola modifica al modo in cui salviamo i dati:
Memorizzare l'indirizzo email del cliente e il tipo di entità come GSI PK e SK;
Garantisce che l'indirizzo email del cliente sia univoco (solo per questa GSI; imporre l'unicità sull'indice primario richiederebbe un'altra soluzione)
Consente, ad esempio, che i clienti compaiano anche come venditori (se avessimo quel tipo di entità)
Ci permette di cercare un cliente per email invece che per ID
Per aiutare la distribuzione dei dati in DynamoDB, potremmo fare un ulteriore passo e memorizzare l'indirizzo email come hash.
Fino ad ora, potresti esserti chiesto perché tutti gli indici avessero nomi generici con attributi chiave genericamente chiamati semplicemente PK e SK. Ora la risposta dovrebbe essere chiara: i dati che memorizziamo e gli indici che manteniamo sono flessibili e in grado di supportare molteplici pattern di accesso a seconda del tipo di entità.
Il terraform per la tabella che abbiamo usato finora è il seguente:
resource "aws_dynamodb_table""tutorial-1" {
name ="tutorial-1"
billing_mode ="PAY_PER_REQUEST"
hash_key ="pk"
range_key ="sk"
attribute {
name ="pk"type="S"
}
attribute {
name ="sk"type="S"
}
attribute {
name ="gsi1_pk"type="S"
}
attribute {
name ="gsi1_sk"type="S"
}
attribute {
name ="gsi2_pk"type="S"
}
attribute {
name ="gsi2_sk"type="S"
}
global_secondary_index {
name ="gsi1"
hash_key ="gsi1_pk"
range_key ="gsi1_sk"
projection_type ="ALL"
}
global_secondary_index {
name ="gsi2"
hash_key ="gsi2_pk"
range_key ="gsi2_sk"
projection_type ="ALL"
}
}
Offerto da
Prodotti
La memorizzazione dei prodotti segue lo stesso pattern uno-a-molti, dove la chiave di partizione è l'ID della categoria e la chiave di ordinamento è l'ID del prodotto:
Aggiungere la categoria padre alla GSI1 ci consente di interrogare tutti i libri nel nostro database:
In un'applicazione in produzione, possiamo usare gli stream di DynamoDB per inviare i dati a un cluster Elasticsearch, ottenendo così la possibilità di cercare per titolo del libro; ma come potremmo usare il modello esistente per interrogare per titolo del prodotto?
Abbiamo già usato GSI1, quindi potremmo usare GSI2 per questo:
Ricorda che, quando interroghi DynamoDB, devi fornire l'intera chiave di partizione esatta, quindi, sebbene questa implementazione possa non essere la più ottimale, illustra come tre indici possano essere usati per diversi pattern di accesso.
Nel mio prossimo tutorial, mostrerò come risolvere questo aspetto integrandolo con un indice di ricerca.
Contenuti del carrello e righe d'ordine
Sia gli articoli del carrello sia le righe d'ordine sono memorizzati nella partizione del cliente; possiamo usare le GSI per consentirci di interrogare tutti i prodotti in un carrello, tutti i prodotti in un ordine e tutti i prodotti che un cliente ha mai ordinato.
Questo ultimo esempio mostra la cronologia ordini di un cliente con due prodotti effettuati da due diversi ID ordine.
Per chi è interessato a come funziona il data modeling nei database SQL, il nostro webinar Data Modeling in SQL offre spunti preziosi che possono completare la comprensione del data modeling NoSQL.
Sfide associate al design a tabella singola
Usare un design a tabella singola con DynamoDB offre molti vantaggi, ma presenta anche diverse sfide e considerazioni:
Complessità e logica applicativa
Gestire tutti i tuoi dati in un'unica tabella può diventare complesso, soprattutto man mano che l'applicazione cresce ed evolve.
Devi pianificare e strutturare con attenzione i dati per accogliere vari pattern di query. Inoltre, l'efficacia di un design a tabella singola è strettamente legata alla logica della tua applicazione; essa svolge un ruolo fondamentale nel garantire un accesso fluido ai dati e un'elaborazione efficiente delle query.
Una considerazione attenta sia della struttura dei dati sia della logica applicativa è essenziale affinché un design a tabella singola funzioni in modo efficace.
Overhead degli indici
Sebbene DynamoDB consenta indici secondari globali per supportare diversi pattern di query, devi predisporre e mantenere questi indici, il che può aumentare costi e complessità.
Scelta della chiave di partizione
Scegliere la giusta chiave di partizione è fondamentale per una distribuzione uniforme dei dati e per le prestazioni delle query. Una chiave di partizione inadatta può portare a partizioni calde e colli di bottiglia prestazionali.
Dimensioni e costi
All'aumentare del volume dei dati, la tua tabella singola può crescere significativamente in dimensioni. Questo può influire sia sul costo di archiviazione sia sul costo di lettura e scrittura dei dati.
Assenza di ricerca full-text
DynamoDB non supporta nativamente la ricerca full-text, quindi implementare tale funzionalità richiede l'integrazione con altri servizi come Elasticsearch.
Modifiche allo schema
Apportare modifiche allo schema della tabella può essere impegnativo, soprattutto nei sistemi in produzione. Potresti dover migrare i dati esistenti o modificare la logica applicativa per accogliere i cambiamenti di schema.
Considerazioni sulla consistenza
A seconda della scelta della consistenza di lettura (forte o eventuale), potresti dover tenere conto di potenziali sfide di consistenza dei dati, tenendo presente che le GSI sono replicate in modo asincrono.
Flessibilità di query limitata
Le capacità di query di DynamoDB sono potenti ma non flessibili come le query SQL.
Per comprendere meglio come progettare efficacemente i database e affrontare le sfide associate, dai un'occhiata al nostro corso di Database Design.
Conclusione
Quando lavori con un database NoSQL, è fondamentale allontanarsi dal territorio familiare dei pattern di progettazione dei database relazionali; devi disimparare ciò che hai imparato.
Scegliere il pattern a tabella singola offre la promessa di un database scalabile a livello globale, pronto a gestire grandi volumi di traffico con prestazioni affidabili e prevedibili.
Tuttavia, questo vantaggio prestazionale non è gratuito; richiede una pianificazione meticolosa e un'attenta implementazione della struttura del database e della logica applicativa fin dall'inizio.
Approfondisci le tue conoscenze con il nostro corso sui concetti NoSQL!
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Benvenuto nel mondo di DynamoDB, dove efficienza e scalabilità illimitata convergono in un'unica tabella. In questo tutorial completo, intraprenderemo un percorso stimolante per svelare il vero potenziale del design di database NoSQL a tabella singola.
Scopri l'arte di semplificare modelli di dati complessi sfruttando tutta la potenza di una struttura unificata, in grado di accogliere senza sforzo le tue esigenze in continua crescita.
Che tu sia uno sviluppatore esperto di database NoSQL o che tu stia appena iniziando il tuo percorso nel mondo dei database, preparati a sbloccare un nuovo livello di scalabilità per le tue applicazioni con una potente architettura dati!
Che cos'è il NoSQL?
Prima di iniziare, è importante capire la differenza tra NoSQL e database relazionali, in quali scenari useremmo ciascuno e perché queste tecnologie sono state inventate. Questo richiede una breve occhiata alla storia dell'elaborazione dei dati.
Puoi leggere di più sull'importanza dei database NoSQL nella data science in un post separato.
Database relazionali
Il database relazionale è una tecnologia ben nota e ampiamente utilizzata sin dagli anni '70. Due dei principali problemi che il database relazionale risolve sono l'archiviazione e l'integrità referenziale.
I dati sono "normalizzati", tipicamente alla terza forma normale, il che riduce l'impronta dei dati e garantisce che eventuali aggiornamenti restino coerenti con il resto dei dati.
La dimensione dell'impronta dei dati persistenti era cruciale perché, storicamente, il componente più costoso nel data center era il disco rigido. Oggi non è più così: la CPU è diventata la componente più costosa e l'archiviazione dei dati si è drasticamente ridotta di costo. Questo non significa che il database relazionale sia obsoleto; ha applicazioni valide per:
Database NoSQL
La pressione sui dati è la capacità di un sistema di elaborare una determinata quantità di dati a costi e/o tempi ragionevoli.
I database NoSQL sono stati inventati alla fine degli anni '90 per affrontare i problemi di pressione dei dati che influivano sulle prestazioni dei database relazionali a causa dei volumi sempre crescenti di dati applicativi.
La CPU in un database relazionale può essere considerata uno dei principali colli di bottiglia, poiché il server relazionale si affanna a recuperare e ricomporre dati normalizzati nelle viste denormalizzate che le applicazioni in genere consumano.
Questo può essere alleviato aggiungendo server replica di sola lettura, ma NoSQL adotta un approccio diverso e memorizza i dati applicativi in forma denormalizzata, pronta per essere consumata dalle applicazioni. Ciò significa che l'impronta dei dati è maggiore rispetto a un database relazionale, ma alleggerisce il carico sulla CPU, rendendo di fatto il server del database un semplice router che usa un algoritmo di hash per puntare a una posizione su un disco nel data center. Gli utilizzi tipici dei database NoSQL sono:
Numerosi studi ci hanno fornito abbastanza dati oggettivi sull'argomento per presentare un grafico che rappresenta le prestazioni di SQL vs NoSQL su larga scala.
Fonte dell'immagine
L'ultimo punto da chiarire è che i dati archiviati in un database NoSQL sono comunque dati relazionali. Se i dati non fossero relazionali, li archivieremmo in un semplice filestore.
Antipattern NoSQL
Non modelliamo un database NoSQL nello stesso modo in cui modelliamo un database relazionale, quindi è un antipattern tradurre un design normalizzato direttamente in un database NoSQL usando più tabelle collegate tra loro, perché NoSQL non ha l'operatore di join né l'integrità referenziale.
Una tabella in NoSQL è l'equivalente di un catalogo in un database relazionale. Invece, modella i dati per essere memorizzati in forma denormalizzata, pronti per il consumo da parte delle applicazioni.
Un altro antipattern sono le Hot Key. Significa che la maggior parte, se non tutte, le richieste di accesso colpiscono un singolo nodo di archiviazione, il che implica che lo spazio delle chiavi non è stato progettato correttamente.
La heatmap degli accessi apparirà più o meno così:
DynamoDB
AWS DynamoDB è un archivio key-value a colonne larghe, completamente gestito e serverless, il che significa che supporta molti item, o righe, in una tabella, ma questi item non hanno necessariamente gli stessi attributi. I tipi di dato supportati sono i seguenti:
Come detto in precedenza, NoSQL andrebbe usato quando i pattern di accesso dell'applicazione sono ben noti e c'è bisogno di supportare un elevato numero di TPS. DynamoDB può scalare per qualsiasi carico di lavoro e garantisce tempi di risposta prevedibilmente rapidi e costanti fino a 4 milioni di TPS con bassa latenza (10 - 20 ms).
Come funziona DynamoDB?
Ogni item (o riga) ha una chiave di partizione che lo identifica in modo univoco e determina la distribuzione dei dati sull'archiviazione sottostante.
Gli item possono avere facoltativamente una chiave di ordinamento, che determina l'ordine in cui i dati sono memorizzati su disco all'interno di una partizione. Le chiavi di ordinamento ci consentono di interrogare le partizioni con operazioni complesse di range e filtro (anche se i filtri sono applicati a livello di client, non di database).
La chiave di partizione viene usata per creare un indice hash e poi ogni item viene disposto secondo questo hash su uno spazio chiavi virtuale. Questo spazio chiavi virtuale è diviso in segmenti che mappano su dispositivi di archiviazione fisici, che possono crescere e ridursi dinamicamente a seconda del volume di dati memorizzati.
Ecco perché DynamoDB è veloce e coerente a qualsiasi scala: perché il motore del database agisce come un servizio di instradamento dalle chiavi con hash allo storage fisico.
Data modeling con NoSQL
In questo tutorial modelleremo un negozio online chiamato Daintree.com.
(Curiosità: la foresta pluviale di Daintree, nel Queensland, Australia, è una delle più antiche al mondo, con un'età stimata di circa 180 milioni di anni!)
Per prima cosa, creiamo un diagramma ERD (entity relationship diagram) molto semplificato per visualizzare ciò che andremo a modellare.
Da questo, possiamo dedurre che un cliente può creare un carrello e aggiungere più prodotti con quantità variabili.
Un prodotto ha una categoria, che a sua volta può avere categorie padre.
I carrelli possono diventare ordini che possono essere suddivisi in più pagamenti.
Come già detto, questa è una visione altamente semplificata delle relazioni e degli attributi delle entità che compongono un'applicazione di shopping online, ma è sufficiente allo scopo di questo tutorial.
Definire i pattern di accesso
Non dobbiamo preoccuparci di questo passaggio quando implementiamo un database relazionale. SQL è un linguaggio incredibilmente potente che, a patto che lo schema sia stato progettato correttamente, ci consente la flessibilità di interrogare e manipolare i dati come meglio crediamo. Non è così quando si usa il pattern a tabella singola in NoSQL, perché dobbiamo archiviare i dati in forma denormalizzata e pronti per essere consumati direttamente dalla nostra applicazione.
Ecco i pattern di accesso che potremmo voler supportare per la nostra applicazione di shopping online:
Modellare la tabella
NoSQL workbench è uno strumento gratuito fornito da AWS per modellare tabelle NoSQL per DynamoDB.
Vai al Data modeler e crea un nuovo modello di dati con una nuova tabella:
Terremo gli attributi della chiave primaria come stringhe generiche PK e SK:
Per gli attributi aggiuntivi, memorizzeremo l'entity_type insieme a due attributi di chiave primaria per le GSI PK e SK.
Una GSI è un'indice secondario globale, cioè una copia della tua tabella che DynamoDB mantiene sincronizzata (in modo asincrono!!) ma archiviata usando coppie alternative di attributi di chiave primaria. Questo è il meccanismo chiave con cui una tabella singola può supportare molteplici pattern di accesso. Ne parleremo più avanti; per ora, ti basta capire che queste chiavi memorizzeranno le chiavi primarie delle entità correlate.
Gli altri attributi aggiuntivi da aggiungere sono tutti gli attributi non chiave dei nostri dati (nota che questo passaggio è necessario solo in fase di progettazione e non verrà definito esplicitamente quando effettivamente approvvigioneremo la tabella):
Successivamente, aggiungiamo le due GSI utilizzando le chiavi definite nel passaggio precedente:
Clicca su salva e poi su visualizza il modello di dati. Ora dovresti vedere la tabella appena creata ma vuota. Clicca su modifica tabella, quindi in alto a destra su modifica dati; questo ti permetterà di aggiungere nuove righe:
Prefissi dei dati
Prima di iniziare ad aggiungere dati, dobbiamo definire i prefissi della chiave primaria per ogni entità:
prodotto
p#
categoria
c#
cliente (utente)
u#
carrello
b#
articolo del carrello
bi#
ordine
o#
riga d'ordine
ol#
pagamento
py#
fattura
i#
Modellare i dati
Dati del cliente
La maggior parte dei pattern di accesso ruota attorno al cliente, quindi per il nostro indice primario useremo l'ID cliente come partizione principale, con il record effettivo del cliente identificabile grazie alla chiave di ordinamento che è anch'essa l'ID cliente.
Possiamo poi aggiungere facilmente le relazioni cliente-ordini e cliente-carrelli usando la stessa chiave di partizione ma con una chiave di ordinamento univoca.
La nostra vista aggregata ora appare così:
Ora possiamo interrogare tutti e tre i record con un unico comando, interrogando l'intera partizione di quel cliente:
Man mano che la tabella cresce, questo tipo di query potrebbe non essere il più efficiente, dato che restituirà ogni ordine del cliente mai effettuato insieme a tutti gli altri record del cliente, come carrelli e fatture, ma è utile sapere che possiamo accedere a tutti i dati di un cliente con una singola query se necessario.
Un piccolo aggiustamento ai valori dell'espressione degli attributi per includere una corrispondenza parziale (begins_with) sulla chiave di ordinamento ci consente di interrogare:
Il punto chiave è come possiamo modellare relazioni uno-a-uno e uno-a-molti in NoSQL.
Categorie
Queste entità rappresentano un'altra relazione uno-a-molti in cui una categoria può contenere molti prodotti.
Le categorie hanno anche una relazione gerarchica in cui una categoria può avere molte sottocategorie, il che significa che i prodotti possono esistere ovunque nell'albero gerarchico delle categorie.
Modelliamo questo con i libri suddivisi in narrativa e saggistica.
Nota come abbiamo popolato gli attributi chiave della GSI per le sottocategorie, impostando GSI1_PK come l'ID della categoria padre e GSI1_SK come l'ID della sottocategoria. Questo ci permette di interrogare tutte le sottocategorie per categoria usando GSI1:
Lo stesso indice, GSI1, può essere usato non solo per mappare relazioni uno-a-molti. Poniamo per esempio di voler cercare il cliente per email; l'attuale modello non lo supporta direttamente a meno che non apportiamo una piccola modifica al modo in cui salviamo i dati:
Memorizzare l'indirizzo email del cliente e il tipo di entità come GSI PK e SK;
Per aiutare la distribuzione dei dati in DynamoDB, potremmo fare un ulteriore passo e memorizzare l'indirizzo email come hash.
Fino ad ora, potresti esserti chiesto perché tutti gli indici avessero nomi generici con attributi chiave genericamente chiamati semplicemente PK e SK. Ora la risposta dovrebbe essere chiara: i dati che memorizziamo e gli indici che manteniamo sono flessibili e in grado di supportare molteplici pattern di accesso a seconda del tipo di entità.
Il terraform per la tabella che abbiamo usato finora è il seguente:
Prodotti
La memorizzazione dei prodotti segue lo stesso pattern uno-a-molti, dove la chiave di partizione è l'ID della categoria e la chiave di ordinamento è l'ID del prodotto:
Aggiungere la categoria padre alla GSI1 ci consente di interrogare tutti i libri nel nostro database:
In un'applicazione in produzione, possiamo usare gli stream di DynamoDB per inviare i dati a un cluster Elasticsearch, ottenendo così la possibilità di cercare per titolo del libro; ma come potremmo usare il modello esistente per interrogare per titolo del prodotto?
Abbiamo già usato GSI1, quindi potremmo usare GSI2 per questo:
Ricorda che, quando interroghi DynamoDB, devi fornire l'intera chiave di partizione esatta, quindi, sebbene questa implementazione possa non essere la più ottimale, illustra come tre indici possano essere usati per diversi pattern di accesso.
Nel mio prossimo tutorial, mostrerò come risolvere questo aspetto integrandolo con un indice di ricerca.
Contenuti del carrello e righe d'ordine
Sia gli articoli del carrello sia le righe d'ordine sono memorizzati nella partizione del cliente; possiamo usare le GSI per consentirci di interrogare tutti i prodotti in un carrello, tutti i prodotti in un ordine e tutti i prodotti che un cliente ha mai ordinato.
Questo ultimo esempio mostra la cronologia ordini di un cliente con due prodotti effettuati da due diversi ID ordine.
Per chi è interessato a come funziona il data modeling nei database SQL, il nostro webinar Data Modeling in SQL offre spunti preziosi che possono completare la comprensione del data modeling NoSQL.
Sfide associate al design a tabella singola
Usare un design a tabella singola con DynamoDB offre molti vantaggi, ma presenta anche diverse sfide e considerazioni:
Complessità e logica applicativa
Gestire tutti i tuoi dati in un'unica tabella può diventare complesso, soprattutto man mano che l'applicazione cresce ed evolve.
Devi pianificare e strutturare con attenzione i dati per accogliere vari pattern di query. Inoltre, l'efficacia di un design a tabella singola è strettamente legata alla logica della tua applicazione; essa svolge un ruolo fondamentale nel garantire un accesso fluido ai dati e un'elaborazione efficiente delle query.
Una considerazione attenta sia della struttura dei dati sia della logica applicativa è essenziale affinché un design a tabella singola funzioni in modo efficace.
Overhead degli indici
Sebbene DynamoDB consenta indici secondari globali per supportare diversi pattern di query, devi predisporre e mantenere questi indici, il che può aumentare costi e complessità.
Scelta della chiave di partizione
Scegliere la giusta chiave di partizione è fondamentale per una distribuzione uniforme dei dati e per le prestazioni delle query. Una chiave di partizione inadatta può portare a partizioni calde e colli di bottiglia prestazionali.
Dimensioni e costi
All'aumentare del volume dei dati, la tua tabella singola può crescere significativamente in dimensioni. Questo può influire sia sul costo di archiviazione sia sul costo di lettura e scrittura dei dati.
Assenza di ricerca full-text
DynamoDB non supporta nativamente la ricerca full-text, quindi implementare tale funzionalità richiede l'integrazione con altri servizi come Elasticsearch.
Modifiche allo schema
Apportare modifiche allo schema della tabella può essere impegnativo, soprattutto nei sistemi in produzione. Potresti dover migrare i dati esistenti o modificare la logica applicativa per accogliere i cambiamenti di schema.
Considerazioni sulla consistenza
A seconda della scelta della consistenza di lettura (forte o eventuale), potresti dover tenere conto di potenziali sfide di consistenza dei dati, tenendo presente che le GSI sono replicate in modo asincrono.
Flessibilità di query limitata
Le capacità di query di DynamoDB sono potenti ma non flessibili come le query SQL.
Per comprendere meglio come progettare efficacemente i database e affrontare le sfide associate, dai un'occhiata al nostro corso di Database Design.
Conclusione
Quando lavori con un database NoSQL, è fondamentale allontanarsi dal territorio familiare dei pattern di progettazione dei database relazionali; devi disimparare ciò che hai imparato.
Scegliere il pattern a tabella singola offre la promessa di un database scalabile a livello globale, pronto a gestire grandi volumi di traffico con prestazioni affidabili e prevedibili.
Tuttavia, questo vantaggio prestazionale non è gratuito; richiede una pianificazione meticolosa e un'attenta implementazione della struttura del database e della logica applicativa fin dall'inizio.
Approfondisci le tue conoscenze con il nostro corso sui concetti NoSQL!