Fondamenti di progettazione di database: confronto tra PostgreSQL, Cassandra e MongoDB

Ciao amici. Prima di partire per la seconda parte delle vacanze di maggio, condividiamo con voi il materiale che abbiamo tradotto in previsione del lancio di un nuovo stream sul corso "DBMS relazionale".

Fondamenti di progettazione di database: confronto tra PostgreSQL, Cassandra e MongoDB

Gli sviluppatori di applicazioni trascorrono molto tempo confrontando più database operativi per selezionare quello più adatto al carico di lavoro previsto. Le esigenze possono includere modellazione dei dati semplificata, garanzie transazionali, prestazioni di lettura/scrittura, scalabilità orizzontale e tolleranza agli errori. Tradizionalmente, la scelta inizia con la categoria del database, SQL o NoSQL, poiché ciascuna categoria presenta una serie chiara di compromessi. Le prestazioni elevate in termini di bassa latenza e throughput elevato sono generalmente viste come un requisito senza compromessi e sono quindi essenziali per qualsiasi database di esempio.

Lo scopo di questo articolo è aiutare gli sviluppatori di applicazioni a fare la scelta giusta tra SQL e NoSQL nel contesto della modellazione dei dati delle applicazioni. Esamineremo un database SQL, ovvero PostgreSQL, e due database NoSQL, Cassandra e MongoDB, per coprire le basi della progettazione di database, come creare tabelle, popolarle, leggere dati da una tabella ed eliminarla. Nel prossimo articolo esamineremo sicuramente indici, transazioni, JOIN, direttive TTL e progettazione di database basata su JSON.

Qual è la differenza tra SQL e NoSQL?

I database SQL aumentano la flessibilità dell'applicazione attraverso le garanzie transazionali ACID, nonché la loro capacità di eseguire query sui dati utilizzando JOIN in modi inaspettati oltre ai modelli di database relazionali normalizzati esistenti.

Data la loro architettura monolitica/a nodo singolo e l'uso di un modello di replica master-slave per la ridondanza, i database SQL tradizionali mancano di due caratteristiche importanti: scalabilità di scrittura lineare (ovvero partizionamento automatico su più nodi) e perdita di dati automatica/zero. Ciò significa che la quantità di dati ricevuti non può superare il throughput massimo di scrittura di un singolo nodo. Inoltre, una certa perdita temporanea di dati deve essere presa in considerazione nella tolleranza agli errori (in un'architettura senza condivisione). Qui è necessario tenere presente che i commit recenti non si sono ancora riflessi nella copia slave. Anche gli aggiornamenti senza tempi di inattività sono difficili da ottenere nei database SQL.

I database NoSQL sono generalmente distribuiti per natura, ad es. in essi, i dati sono divisi in sezioni e distribuiti su più nodi. Richiedono la denormalizzazione. Ciò significa che i dati inseriti dovranno essere copiati anche più volte per rispondere alle specifiche richieste da te inviate. L'obiettivo generale è ottenere prestazioni elevate riducendo il numero di shard disponibili durante le letture. Ciò implica che NoSQL richiede che tu modelli le tue query, mentre SQL richiede che tu modelli i tuoi dati.

NoSQL si concentra sul raggiungimento di prestazioni elevate in un cluster distribuito e questa è la logica alla base di molti compromessi nella progettazione di database che includono perdita di transazioni ACID, JOIN e indici secondari globali coerenti.

Si sostiene che, sebbene i database NoSQL forniscano scalabilità di scrittura lineare e elevata tolleranza agli errori, la perdita di garanzie transazionali li rende inadatti per dati mission-critical.

La tabella seguente mostra le differenze tra la modellazione dei dati in NoSQL e SQL.

Fondamenti di progettazione di database: confronto tra PostgreSQL, Cassandra e MongoDB

SQL e NoSQL: perché sono necessari entrambi?

Le applicazioni del mondo reale con un gran numero di utenti, come Amazon.com, Netflix, Uber e Airbnb, hanno il compito di eseguire attività complesse e sfaccettate. Ad esempio, un'applicazione di e-commerce come Amazon.com deve archiviare dati leggeri e altamente critici come informazioni sugli utenti, prodotti, ordini, fatture, insieme a dati pesanti e meno sensibili come recensioni di prodotti, messaggi di supporto, attività degli utenti, recensioni e consigli degli utenti. Naturalmente queste applicazioni si basano su almeno un database SQL insieme ad almeno un database NoSQL. Nei sistemi interregionali e globali, un database NoSQL funziona come una cache geograficamente distribuita per i dati archiviati in un database SQL di origine attendibile in esecuzione in una particolare regione.

In che modo YugaByte DB combina SQL e NoSQL?

Costruito su un motore di archiviazione misto orientato ai log, partizionamento automatico, replica di consenso distribuito frammentato e transazioni distribuite ACID (ispirate a Google Spanner), YugaByte DB è il primo database open source al mondo che è contemporaneamente compatibile con NoSQL (Cassandra e Redis) e SQL (PostgreSQL). Come mostrato nella tabella seguente, YCQL, l'API YugaByte DB compatibile con Cassandra, aggiunge i concetti di transazioni ACID a chiave singola e multichiave e indici secondari globali all'API NoSQL, inaugurando così l'era dei database NoSQL transazionali. Inoltre, YCQL, l'API YugaByte DB compatibile con PostgreSQL, aggiunge i concetti di scalabilità lineare della scrittura e tolleranza agli errori automatica all'API SQL, portando i database SQL distribuiti nel mondo. Poiché YugaByte DB è di natura transazionale, l'API NoSQL può ora essere utilizzata nel contesto di dati mission-critical.

Fondamenti di progettazione di database: confronto tra PostgreSQL, Cassandra e MongoDB

Come già detto nell'articolo "Presentazione di YSQL: un'API SQL distribuita compatibile con PostgreSQL per YugaByte DB", la scelta tra SQL o NoSQL nel DB YugaByte dipende interamente dalle caratteristiche del carico di lavoro sottostante:

  • Se il tuo carico di lavoro principale è costituito da operazioni JOIN multi-chiave, quando scegli YSQL, tieni presente che le tue chiavi potrebbero essere distribuite su più nodi, con conseguente latenza più elevata e/o throughput inferiore rispetto a NoSQL.
  • Altrimenti, scegli una delle due API NoSQL, tenendo presente che otterrai prestazioni migliori come risultato delle query servite da un nodo alla volta. YugaByte DB può fungere da unico database operativo per applicazioni complesse del mondo reale che devono gestire più carichi di lavoro contemporaneamente.

Il laboratorio di modellazione dei dati nella sezione successiva si basa sui database YugaByte DB compatibili con le API PostgreSQL e Cassandra, anziché sui database nativi. Questo approccio enfatizza la facilità di interazione con due API diverse (su due porte diverse) dello stesso cluster di database, invece di utilizzare cluster completamente indipendenti di due database diversi.
Nelle sezioni seguenti, daremo uno sguardo al laboratorio di modellazione dei dati per illustrare le differenze e alcuni punti in comune dei database trattati.

Laboratorio di modellazione dei dati

Installazione della banca dati

Data l'enfasi sulla progettazione del modello di dati (piuttosto che su complesse architetture di distribuzione), installeremo i database in contenitori Docker sul computer locale e quindi interagiremo con essi utilizzando le rispettive shell della riga di comando.

Database YugaByte DB compatibile con PostgreSQL e Cassandra

mkdir ~/yugabyte && cd ~/yugabyte
wget https://downloads.yugabyte.com/yb-docker-ctl && chmod +x yb-docker-ctl
docker pull yugabytedb/yugabyte
./yb-docker-ctl create --enable_postgres

MongoDB

docker run --name my-mongo -d mongo:latest

Accesso alla riga di comando

Colleghiamoci ai database utilizzando la shell della riga di comando per le API corrispondenti.

PostgreSQL

psql è una shell a riga di comando per interagire con PostgreSQL. Per facilità d'uso, YugaByte DB viene fornito con psql direttamente nella cartella bin.

docker exec -it yb-postgres-n1 /home/yugabyte/postgres/bin/psql -p 5433 -U postgres

Cassandra

cqlsh è una shell a riga di comando per interagire con Cassandra e i suoi database compatibili tramite CQL (Cassandra Query Language). Per facilità d'uso, YugaByte DB viene fornito con cqlsh nel catalogo bin.
Tieni presente che CQL è stato ispirato da SQL e ha concetti simili di tabelle, righe, colonne e indici. Tuttavia, essendo un linguaggio NoSQL, aggiunge una certa serie di limitazioni, la maggior parte delle quali tratteremo anche in altri articoli.

docker exec -it yb-tserver-n1 /home/yugabyte/bin/cqlsh

MongoDB

mongo è una shell a riga di comando per interagire con MongoDB. Può essere trovato nella directory bin dell'installazione di MongoDB.

docker exec -it my-mongo bash 
cd bin
mongo

Crea una tabella

Ora possiamo interagire con il database per eseguire varie operazioni utilizzando la riga di comando. Iniziamo creando una tabella che memorizza le informazioni sulle canzoni scritte da diversi artisti. Queste canzoni potrebbero far parte di un album. Anche gli attributi opzionali per una canzone sono l'anno di uscita, il prezzo, il genere e la valutazione. Dobbiamo tenere conto degli attributi aggiuntivi che potrebbero essere necessari in futuro attraverso il campo "tag". Può archiviare dati semistrutturati sotto forma di coppie chiave-valore.

PostgreSQL

CREATE TABLE Music (
    Artist VARCHAR(20) NOT NULL, 
    SongTitle VARCHAR(30) NOT NULL,
    AlbumTitle VARCHAR(25),
    Year INT,
    Price FLOAT,
    Genre VARCHAR(10),
    CriticRating FLOAT,
    Tags TEXT,
    PRIMARY KEY(Artist, SongTitle)
);	

Cassandra

La creazione di una tabella in Cassandra è molto simile a PostgreSQL. Una delle differenze principali è la mancanza di vincoli di integrità (ad esempio NOT NULL), ma questa è responsabilità dell'applicazione, non del database NoSQL. La chiave primaria è costituita da una chiave di partizione (la colonna Artista nell'esempio seguente) e da un insieme di colonne di clustering (la colonna SongTitle nell'esempio seguente). La chiave di partizione determina in quale partizione/frammento deve essere posizionata la riga e le colonne di clustering indicano come devono essere organizzati i dati all'interno del frammento corrente.

CREATE KEYSPACE myapp;
USE myapp;
CREATE TABLE Music (
    Artist TEXT, 
    SongTitle TEXT,
    AlbumTitle TEXT,
    Year INT,
    Price FLOAT,
    Genre TEXT,
    CriticRating FLOAT,
    Tags TEXT,
    PRIMARY KEY(Artist, SongTitle)
);

MongoDB

MongoDB organizza i dati in database (Database) (simili al Keyspace di Cassandra), dove sono presenti Collezioni (simili a tabelle) che contengono Documenti (simili alle righe di una tabella). In MongoDB non è praticamente necessario definire uno schema iniziale. Squadra "usa banca dati", mostrato di seguito, crea un'istanza del database alla prima chiamata e modifica il contesto per il database appena creato. Anche le raccolte non necessitano di essere create esplicitamente; vengono create automaticamente, semplicemente quando aggiungi il primo documento a una nuova raccolta. Tieni presente che MongoDB utilizza il database di test per impostazione predefinita, quindi qualsiasi operazione a livello di raccolta senza specificare un database specifico verrà eseguita su di esso per impostazione predefinita.

use myNewDatabase;

Ottenere informazioni su una tabella
PostgreSQL

d Music
Table "public.music"
    Column    |         Type          | Collation | Nullable | Default 
--------------+-----------------------+-----------+----------+--------
 artist       | character varying(20) |           | not null | 
 songtitle    | character varying(30) |           | not null | 
 albumtitle   | character varying(25) |           |          | 
 year         | integer               |           |          | 
 price        | double precision      |           |          | 
 genre        | character varying(10) |           |          | 
 criticrating | double precision      |           |          | 
 tags         | text                  |           |          | 
Indexes:
    "music_pkey" PRIMARY KEY, btree (artist, songtitle)

Cassandra

DESCRIBE TABLE MUSIC;
CREATE TABLE myapp.music (
    artist text,
    songtitle text,
    albumtitle text,
    year int,
    price float,
    genre text,
    tags text,
    PRIMARY KEY (artist, songtitle)
) WITH CLUSTERING ORDER BY (songtitle ASC)
    AND default_time_to_live = 0
    AND transactions = {'enabled': 'false'};

MongoDB

use myNewDatabase;
show collections;

Immissione di dati in una tabella
PostgreSQL

INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Year, Price, Genre, CriticRating, 
    Tags)
VALUES(
    'No One You Know', 'Call Me Today', 'Somewhat Famous',
    2015, 2.14, 'Country', 7.8,
    '{"Composers": ["Smith", "Jones", "Davis"],"LengthInSeconds": 214}'
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre, CriticRating)
VALUES(
    'No One You Know', 'My Dog Spot', 'Hey Now',
    1.98, 'Country', 8.4
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre)
VALUES(
    'The Acme Band', 'Look Out, World', 'The Buck Starts Here',
    0.99, 'Rock'
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre, 
    Tags)
VALUES(
    'The Acme Band', 'Still In Love', 'The Buck Starts Here',
    2.47, 'Rock', 
    '{"radioStationsPlaying": ["KHCR", "KBQX", "WTNR", "WJJH"], "tourDates": { "Seattle": "20150625", "Cleveland": "20150630"}, "rotation": Heavy}'
);

Cassandra

Espressione complessiva INSERT in Cassandra sembra molto simile a quello in PostgreSQL. Tuttavia, c’è una grande differenza nella semantica. A Cassandra INSERT è in realtà un'operazione UPSERT, dove gli ultimi valori vengono aggiunti alla riga se la riga esiste già.

L'immissione dei dati è simile a PostgreSQL INSERT sopra

.

MongoDB

Anche se MongoDB è un database NoSQL come Cassandra, la sua operazione di inserimento non ha nulla in comune con il comportamento semantico di Cassandra. In MongoDB inserire() non ha opportunità UPSERT, che lo rende simile a PostgreSQL. Aggiunta di dati predefiniti senza _idspecified farà sì che un nuovo documento venga aggiunto alla raccolta.

db.music.insert( {
artist: "No One You Know",
songTitle: "Call Me Today",
albumTitle: "Somewhat Famous",
year: 2015,
price: 2.14,
genre: "Country",
tags: {
Composers: ["Smith", "Jones", "Davis"],
LengthInSeconds: 214
}
}
);
db.music.insert( {
artist: "No One You Know",
songTitle: "My Dog Spot",
albumTitle: "Hey Now",
price: 1.98,
genre: "Country",
criticRating: 8.4
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Look Out, World",
albumTitle:"The Buck Starts Here",
price: 0.99,
genre: "Rock"
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Still In Love",
albumTitle:"The Buck Starts Here",
price: 2.47,
genre: "Rock",
tags: {
radioStationsPlaying:["KHCR", "KBQX", "WTNR", "WJJH"],
tourDates: {
Seattle: "20150625",
Cleveland: "20150630"
},
rotation: "Heavy"
}
}
);

Interrogazione tabella

Forse la differenza più significativa tra SQL e NoSQL in termini di costruzione delle query è il linguaggio utilizzato FROM и WHERE. SQL consente l'espressione successiva FROM seleziona più tabelle ed espressione con WHERE può essere di qualsiasi complessità (comprese le operazioni JOIN tra i tavoli). Tuttavia, NoSQL tende a imporre una severa limitazione FROMe funziona solo con una tabella specificata e in WHERE, la chiave primaria deve essere sempre specificata. Ciò si collega alla spinta prestazionale NoSQL di cui abbiamo parlato in precedenza. Questo desiderio porta a ogni possibile riduzione di qualsiasi interazione tra tabelle e chiavi. Può introdurre un notevole ritardo nella comunicazione tra nodi quando si risponde a una richiesta ed è quindi meglio evitarlo in generale. Ad esempio, Cassandra richiede che le query siano limitate a determinati operatori (only =, IN, <, >, =>, <=) sulle chiavi di partizione, tranne quando si richiede un indice secondario (qui è consentito solo l'operatore =).

PostgreSQL

Di seguito sono riportati tre esempi di query che possono essere facilmente eseguite da un database SQL.

  • Visualizza tutte le canzoni per artista;
  • Visualizza tutti i brani dell'artista che corrispondono alla prima parte del titolo;
  • Visualizza tutte le canzoni di un artista che hanno una determinata parola nel titolo e hanno un prezzo inferiore a 1.00.
SELECT * FROM Music
WHERE Artist='No One You Know';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle LIKE 'Call%';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle LIKE '%Today%'
AND Price > 1.00;

Cassandra

Delle query PostgreSQL elencate sopra, solo la prima funzionerà invariata in Cassandra, poiché l'operatore LIKE non può essere applicato a colonne di clustering come SongTitle. In questo caso sono ammessi solo gli operatori = и IN.

SELECT * FROM Music
WHERE Artist='No One You Know';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle IN ('Call Me Today', 'My Dog Spot')
AND Price > 1.00;

MongoDB

Come mostrato negli esempi precedenti, il metodo principale per creare query in MongoDB è db.raccolta.find(). Questo metodo contiene esplicitamente il nome della raccolta (music nell'esempio seguente), quindi è vietato eseguire query su più raccolte.

db.music.find( {
  artist: "No One You Know"
 } 
);
db.music.find( {
  artist: "No One You Know",
  songTitle: /Call/
 } 
);

Leggere tutte le righe di una tabella

La lettura di tutte le righe è semplicemente un caso speciale del modello di query esaminato in precedenza.

PostgreSQL

SELECT * 
FROM Music;

Cassandra

Simile all'esempio PostgreSQL sopra.

MongoDB

db.music.find( {} );

Modifica dei dati in una tabella

PostgreSQL

PostgreSQL fornisce istruzioni UPDATE per modificare i dati. Non ha opportunità UPSERT, quindi questa istruzione fallirà se la riga non è più nel database.

UPDATE Music
SET Genre = 'Disco'
WHERE Artist = 'The Acme Band' AND SongTitle = 'Still In Love';

Cassandra

Cassandra sì UPDATE simile a PostgreSQL. UPDATE ha la stessa semantica UPSERT, Piace INSERT.

Simile all'esempio PostgreSQL sopra.

MongoDB
Operazione aggiornare() in MongoDB puoi aggiornare completamente un documento esistente o aggiornare solo determinati campi. Per impostazione predefinita, aggiorna solo un documento con la semantica disabilitata UPSERT. Aggiornamento di più documenti e comportamenti simili UPSERT può essere applicato impostando flag aggiuntivi per l'operazione. Ad esempio, nell'esempio seguente, il genere di un artista specifico viene aggiornato in base alla sua canzone.

db.music.update(
  {"artist": "The Acme Band"},
  { 
    $set: {
      "genre": "Disco"
    }
  },
  {"multi": true, "upsert": true}
);

Rimozione di dati da una tabella

PostgreSQL

DELETE FROM Music
WHERE Artist = 'The Acme Band' AND SongTitle = 'Look Out, World';

Cassandra

Simile all'esempio PostgreSQL sopra.

MongoDB

MongoDB ha due tipi di operazioni per eliminare i documenti − eliminaUno() /deleteMany() и rimuovere(). Entrambi i tipi eliminano i documenti ma restituiscono risultati diversi.

db.music.deleteMany( {
        artist: "The Acme Band"
    }
);

Eliminazione di una tabella

PostgreSQL

DROP TABLE Music;

Cassandra

Simile all'esempio PostgreSQL sopra.

MongoDB

db.music.drop();

conclusione

Il dibattito sulla scelta tra SQL e NoSQL infuria da più di 10 anni. Ci sono due aspetti principali in questo dibattito: l'architettura del motore di database (SQL monolitico, transazionale rispetto a NoSQL distribuito e non transazionale) e l'approccio alla progettazione del database (modellazione dei dati in SQL rispetto alla modellazione delle query in NoSQL).

Con un database transazionale distribuito come YugaByte DB, il dibattito sull'architettura del database può essere facilmente messo a tacere. Man mano che i volumi di dati diventano più grandi di quelli che possono essere scritti su un singolo nodo, diventa necessaria un'architettura completamente distribuita che supporti la scalabilità di scrittura lineare con sharding/ribilanciamento automatico.

Inoltre, come affermato in uno degli articoli Google cloudLe architetture transazionali e fortemente coerenti sono ora più utilizzate per fornire una migliore agilità di sviluppo rispetto alle architetture non transazionali e alla fine coerenti.

Tornando alla discussione sulla progettazione di database, è giusto dire che entrambi gli approcci di progettazione (SQL e NoSQL) sono necessari per qualsiasi applicazione complessa del mondo reale. L'approccio di "modellazione dei dati" SQL consente agli sviluppatori di soddisfare più facilmente i mutevoli requisiti aziendali, mentre l'approccio di "modellazione delle query" NoSQL consente agli stessi sviluppatori di operare su grandi volumi di dati con bassa latenza e throughput elevato. È per questo motivo che YugaByte DB fornisce API SQL e NoSQL in un nucleo comune, anziché promuovere uno degli approcci. Inoltre, fornendo compatibilità con i linguaggi di database più diffusi tra cui PostgreSQL e Cassandra, YugaByte DB garantisce che gli sviluppatori non debbano imparare un altro linguaggio per lavorare con un motore di database distribuito e altamente coerente.

In questo articolo, abbiamo esaminato le differenze tra i fondamenti della progettazione di database tra PostgreSQL, Cassandra e MongoDB. Negli articoli futuri approfondiremo concetti di progettazione avanzati come indici, transazioni, JOIN, direttive TTL e documenti JSON.

Vi auguriamo un buon proseguimento di fine settimana e vi invitiamo a farlo webinar gratuito, che avrà luogo il 14 maggio.

Fonte: habr.com

Aggiungi un commento