I document store NoSQL possono essere ideali per gestire grandi quantità di dati non strutturati. Tuttavia, alcune organizzazioni lavorano con dati non strutturati ma desiderano comunque le funzionalità offerte dai database SQL tradizionali. Ad esempio, le agenzie di stampa o di contenuti giornalistici possono gestire siti web ad alto traffico incentrati su grandi quantità di contenuti testuali e immagini. Anche se hanno bisogno di archiviare questi dati non strutturati, forse non hanno bisogno degli schemi flessibili o della scalabilità orizzontale dei database NoSQL. Hanno invece bisogno della facilità di gestione del database e della coerenza di un database relazionale come PostgreSQL.
È possibile ottenere il meglio dei due mondi? Sì.
Con i suoi tipi di dati pensati per supportare i dati non strutturati, PostgreSQL offre un mezzo felice, consentendo di sfruttare le capacità NoSQL all'interno di un database relazionale che è economico e semplice da gestire. In questo articolo vedremo come utilizzare i tipi di dati HStore e JSONB di PostgreSQL per lavorare con i dati non strutturati.
Prima di addentrarci, vediamo brevemente le principali differenze tra i database SQL e NoSQL.
Comprendere SQL e NoSQL
I database SQL e NoSQL hanno ciascuno i propri punti di forza e di debolezza. Per decidere con cognizione di causa quale sia il più adatto a soddisfare le vostre esigenze di dati, è necessario comprendere bene le loro differenze.
I database SQL (relazionali), come PostgreSQL e MySQL, rappresentano i dati con una struttura chiara e prevedibile in tabelle, righe e colonne. Aderiscono alle proprietà ACID (atomicità, consistenza, isolamento e durata), che costituiscono una solida base per l'integrità dei dati, garantendo che le transazioni del database vengano elaborate in modo affidabile.
I database SQL brillano laddove la coerenza e l'integrità dei dati sono cruciali, ad esempio quando si tratta di query complesse e di sistemi transazionali (come le applicazioni finanziarie).
Al contrario, i database NoSQL (document store) si rivolgono a insiemi di dati ampi e variegati, non necessariamente adatti alla rappresentazione tabellare. Esempi di database NoSQL sono MongoDB, Cassandra e Couchbase. I database NoSQL lavorano con schemi flessibili, consentendo alle strutture di dati di evolvere nel tempo. Supportano inoltre la scalabilità orizzontale, distribuendo i dati su più server per migliorare la gestione di grandi carichi di dati e di traffico elevato.
I database NoSQL sono spesso utilizzati in applicazioni in cui la scalabilità è fondamentale, ad esempio per gestire grandi quantità di dati in applicazioni in tempo reale o modelli linguistici (LLM) di grandi dimensioni. I database NoSQL sono utili anche quando si tratta di strutture di dati variegate e in evoluzione, in quanto consentono alle organizzazioni di adattarsi al mutare delle esigenze dei dati.
Perché usare PostgreSQL come archivio di documenti?
PostgreSQL è un database relazionale, quindi può sembrare poco convenzionale considerarlo un'opzione per soddisfare le esigenze NoSQL. Tuttavia, nella vostra situazione potrebbe essere utile utilizzare PostgreSQL come archivio di documenti.
Se le vostre esigenze di archiviazione dei dati sono diverse e richiedono sia l'archiviazione di dati strutturati e conformi ad ACID , sia l' archiviazione flessibile di documenti senza schema, allora potete sfruttare PostgreSQL per combinare modelli relazionali e non relazionali. Oppure, forse si desiderano alcune funzionalità NoSQL, ma anche le garanzie di coerenza dei dati fornite dalle proprietà ACID. Infine, essendo una tecnologia matura con una comunità attiva, PostgreSQL offre un supporto SQL completo, indicizzazione avanzata e ricerca full-text. Queste caratteristiche, unite alle sue capacità NoSQL, rendono PostgreSQL una soluzione versatile per l'archiviazione dei dati.
Limitazioni dell'uso di PostgreSQL per i dati di tipo NoSQL
Nonostante la sua versatilità, PostgreSQL presenta alcune limitazioni rispetto ai database NoSQL tradizionali. Mentre PostgreSQL può scalare verticalmente, non supporta intrinsecamente lo scaling orizzontale o i dati distribuiti con sharding automatico, caratteristiche che i database NoSQL tipicamente offrono. PostgreSQL non offre inoltre ottimizzazioni per alcune strutture di dati NoSQL, come i negozi a colonne larghe o i database a grafo. Infine, PostgreSQL non offre una coerenza sintonizzabile per ottimizzare le prestazioni, cosa che si può ottenere da alcuni database NoSQL.
Quando si prende in considerazione l'utilizzo di PostgreSQL per grandi insiemi di dati non strutturati, è bene sapere che queste limitazioni possono avere un impatto sulle prestazioni e sulla capacità di scalare. Inoltre, la combinazione di operazioni sui dati SQL e NoSQL introduce complessità. Un'attenta pianificazione e comprensione di entrambi i paradigmi vi aiuterà a evitare le potenziali insidie.
Tuttavia, con la giusta comprensione e i giusti casi d'uso, PostgreSQL può essere uno strumento potente, che offre il meglio dei due mondi SQL e NoSQL.
HStore e JSONB in PostgreSQL
Quando consideriamo le possibilità di utilizzare PostgreSQL come soluzione NoSQL, incontriamo tre tipi di dati che offrono funzionalità simili a NoSQL, ma ognuno di essi ha caratteristiche e casi d'uso unici.
- HStore: Questo tipo di dati consente di memorizzare coppie chiave-valore in un singolo valore PostgreSQL. È utile per memorizzare dati semi-strutturati che non hanno uno schema fisso.
- JSONB: Si tratta di una rappresentazione binaria di dati di tipo JSON. Può memorizzare strutture più complesse rispetto a HStore e supporta tutte le funzionalità JSON. JSONB è indicizzabile, il che lo rende una buona scelta per grandi quantità di dati.
- JSON: È simile a JSONB, anche se manca di molte delle capacità e delle efficienze di JSONB. Il tipo di dati JSON memorizza una copia esatta del testo in ingresso, compresi gli spazi bianchi e le chiavi duplicate.
Il tipo di dati JSON viene citato come una scelta valida per memorizzare dati in formato JSON quando non si ha bisogno delle funzionalità complete fornite da JSONB. Tuttavia, nel resto dell'articolo ci concentreremo principalmente su HStore e JSONB.
HStore
La documentazione di PostgreSQL descrive HStore come utile quando si hanno "righe con molti attributi che vengono esaminati raramente, o dati semi-strutturati". Prima di poter lavorare con il tipo di dati HStore, è necessario abilitare l'estensione HStore:
> CREATE EXTENSION hstore;
HStore è rappresentato da zero o più chiavi => valori separati da virgole. L'ordine delle coppie non è significativo o affidabile in uscita.
> SELECT 'foo => bar, prompt => "hello world", pi => 3.14'::hstore;
hstore
-----------------------------------------------------
"pi"=>"3.14", "foo"=>"bar", "prompt"=>"hello world"
(1 row)
Ogni chiave HStore è unica. Se una dichiarazione HStore viene fatta con chiavi duplicate, solo uno dei duplicati verrà memorizzato e non c'è alcuna garanzia su quale sarà.
> SELECT 'key => value1, key => value2'::hstore;
hstore
-----------------
"key"=>"value1"
(1 row)
Con la sua struttura piatta chiave-valore, HStore offre semplicità e rapidità di interrogazione, rendendolo ideale per scenari semplici. Tuttavia, HStore supporta solo dati testuali e non supporta dati annidati, il che lo rende limitato per strutture di dati complesse.
D'altra parte, JSONB può gestire una più ampia varietà di tipi di dati.
JSONB
Il tipo di dati JSONB accetta testo in ingresso formattato in JSON e lo memorizza in un formato binario decomposto. Sebbene questa conversione renda l'input leggermente lento, il risultato è un'elaborazione veloce e un'indicizzazione efficiente. JSONB non conserva gli spazi bianchi né l'ordine delle chiavi degli oggetti.
> SELECT '{"foo": "bar", "pi": 3.14, "nested": { "prompt": "hello", "count": 5 } }'::jsonb;
jsonb
-----------------------------------------------------------------------
{"pi": 3.14, "foo": "bar", "nested": {"count": 5, "prompt": "hello"}}
(1 row)
Se vengono fornite chiavi di oggetti duplicati, viene mantenuto l'ultimo valore.
> SELECT '{"key": "value1", "key": "value2"}'::jsonb;
jsonb
-------------------
{"key": "value2"}
(1 row)
Poiché JSONB supporta strutture complesse e funzionalità JSON complete, è la scelta ideale per dati complessi o annidati, preferibile a HStore o JSON. Tuttavia, l'uso di JSONB introduce un certo sovraccarico di prestazioni e un maggiore utilizzo dello storage rispetto a HStore.
Esempi pratici: Lavorare con HStore e JSONB
Consideriamo alcuni esempi pratici per dimostrare come lavorare con questi tipi di dati. Vedremo la creazione di tabelle, le query e le operazioni di base e l'indicizzazione.
Operazioni di base di HStore
Come per qualsiasi altro tipo di dati, è possibile definire i campi della tabella di dati PostgreSQL come tipo di dati HStore.
> CREATE TABLE articles ( id serial primary key, title varchar(64), meta hstore );
L'inserimento di un record con un attributo HStore si presenta come segue:
> INSERT INTO articles (title, meta)
VALUES (
'Data Types in PostgreSQL',
'format => blog, length => 1350, language => English, license => "Creative Commons"');
> SELECT * FROM articles;
id | title | meta ----+--------------------------+------------------------------------------ 1 | Data Types in PostgreSQL | "format"=>"blog", "length"=>"1350", "license"=>"Creative Commons", "language"=>"English"(1 row)
Con i campi HStore, è possibile recuperare coppie chiave-valore specifiche dal campo, come specificato dalle chiavi fornite:
> SELECT title, meta -> 'license' AS license, meta -> 'format' AS format FROM articles;
title | license | format
---------------------------------+------------------+------------
Data Types in PostgreSQL | Creative Commons | blog
Advanced Querying in PostgreSQL | None | blog
Scaling PostgreSQL | MIT | blog
PostgreSQL Fundamentals | Creative Commons | whitepaper
(4 rows)
È anche possibile eseguire query con criteri basati su valori specifici all'interno di un campo HStore.
> SELECT id, title FROM articles WHERE meta -> 'license' = 'Creative Commons';
id | title
----+--------------------------
1 | Data Types in PostgreSQL
4 | PostgreSQL Fundamentals
(2 rows)
A volte si può desiderare di interrogare solo le righe che contengono una chiave specifica nel campo HStore. Ad esempio, la seguente query restituisce solo le righe in cui il meta HStore contiene la chiave note. A tale scopo, si utilizza l'operatore ?
> SELECT title, meta->'note' AS note FROM articles WHERE meta ? 'note';
title | note
---------------------------------+-----------------
PostgreSQL Fundamentals | hold for review
Advanced Querying in PostgreSQL | needs edit
(2 rows)
Un elenco di operatori e funzioni HStore utili si trova qui. Ad esempio, è possibile estrarre le chiavi di un HStore in un array o convertire un HStore in una rappresentazione JSON.
> SELECT title, akeys(meta) FROM articles where id=1;
title | akeys
--------------------------+----------------------------------
Data Types in PostgreSQL | {format,length,license,language}
(1 row)
> SELECT title, hstore_to_json(meta) FROM articles where id=1;
title | hstore_to_json
--------------------------+------------------------------------------------
Data Types in PostgreSQL | {"format": "blog", "length": "1350", "license": "Creative Commons", "language": "English"}
(1 row)
Operazioni JSONB di base
Lavorare con il tipo di dati JSONB in PostgreSQL è semplice. La creazione di tabelle e l'inserimento di record si presentano come segue:
> CREATE TABLE authors (id serial primary key, name varchar(64), meta jsonb);
> INSERT INTO authors (name, meta) VALUES ('Adam Anderson', '{ "active":true, "expertise": ["databases", "data science"], "country": "UK" }');
Si noti che il meta-campo jsonb è fornito come stringa di testo in formato JSON. PostgreSQL si lamenterà se il valore fornito non è un JSON valido.
> INSERT INTO authors (name, meta) VALUES ('Barbara Brandini', '{ "this is not valid JSON" }');
ERROR: invalid input syntax for type json
A differenza del tipo HStore, JSONB supporta i dati annidati.
> INSERT INTO authors (name, meta) VALUES ('Barbara Brandini', '{ "active":true, "expertise": ["AI/ML"], "country": "CAN", "contact": { "email": "barbara@example.com", "phone": "111-222-3333" } }');
Analogamente a HStore, i campi JSONB possono essere recuperati parzialmente, solo con alcune chiavi. Ad esempio:
> SELECT name, meta -> 'country' AS country FROM authors;
name | country ------------------+--------- Adam Anderson | "UK" Barbara Brandini | "CAN" Charles Cooper | "UK"(3 rows)
Il tipo di dati JSONB ha molti operatori simili a quelli di HStore. Ad esempio, il seguente uso dell'operatore ? recupera solo le righe in cui il campo meta contiene la chiave del contatto.
> SELECT name, meta -> 'active' AS active, meta -> 'contact' AS contact FROM authors WHERE meta ? 'contact';
name | active | contact
------------------+--------+-----------------------------------------------
Barbara Brandini | true | {"email": "barbara@example.com", "phone": "111-222-3333"}
Charles Cooper | false | {"email": "charles@example.com"}
(2 rows)
Lavorare con gli indici
Secondo la documentazione, il tipo di dati HStore "ha il supporto degli indici GiST e GIN per gli operatori @>, ?& e ?|". Per una spiegazione dettagliata delle differenze tra i due tipi di indici, si veda qui. L'indicizzazione per JSONB utilizza gli indici GIN per facilitare la ricerca efficiente di chiavi o coppie chiave-valore.
L'istruzione per creare un indice è quella che ci si aspetterebbe:
> CREATE INDEX idx_hstore ON articles USING GIN(meta);
> CREATE INDEX idx_jsonb ON authors USING GIN(meta);
Struttura SQL con flessibilità NoSQL
Rivediamo il caso d'uso originale citato nell'introduzione. Immaginiamo un'agenzia di contenuti giornalistici che memorizza i suoi articoli in modo simile a quello che si potrebbe fare con un archivio di documenti NoSQL. Forse l'articolo può essere rappresentato in JSON come un array ordinato di oggetti che rappresentano le sezioni, ognuna con contenuto testuale, notazioni e formattazione. Inoltre, a ogni articolo è associata una serie di metadati, i cui attributi sono incoerenti da un articolo all'altro.
La descrizione di cui sopra racchiude la maggior parte delle esigenze NoSQL dell'organizzazione, ma tutto il resto della gestione e dell'organizzazione dei dati si allinea strettamente a un modello di dati relazionale.
Combinando le capacità NoSQL di un tipo di dati come JSONB con i punti di forza tradizionali di PostgreSQL, l'organizzazione può godere di schemi flessibili e di interrogazioni rapide su dati annidati, pur essendo in grado di eseguire operazioni congiunte e di applicare le relazioni tra i dati. I tipi di dati HStore e JSONB di PostgreSQL offrono potenti opzioni agli sviluppatori che necessitano della struttura di un database relazionale ma anche dell'archiviazione dei dati in stile NoSQL.
PostgreSQL in scala
State cercando di supportare l'archiviazione e l'interrogazione dei dati in stile NoSQL, pur rimanendo nell'ambito di un database relazionale tradizionale? Forse la vostra organizzazione gestisce i documenti in modo simile a quello descritto in questo post. O forse state cercando opzioni per gestire l'archiviazione di dati non strutturati per un modello linguistico di grandi dimensioni (LLM) o per qualche altra impresa di AI/ML.
Il cluster PostgreSQL di Linode Marketplace offre il modello e la struttura relazionale di un database SQL e la scalabilità orizzontale di un database NoSQL. Combinando questo con l'uso dei tipi di dati HStore o JSONB, si ottiene una soluzione ibrida ideale per sfruttare le capacità NoSQL mentre si lavora con PostgreSQL.
Commenti