Fundamentele de proiectare a bazelor de date - Compararea PostgreSQL, Cassandra și MongoDB

Bună prieteni. Înainte de a pleca în a doua parte a sărbătorilor de mai, vă împărtășim materialul pe care l-am tradus în așteptarea lansării unui nou flux pe curs „SGBD relațional”.

Fundamentele de proiectare a bazelor de date - Compararea PostgreSQL, Cassandra și MongoDB

Dezvoltatorii de aplicații petrec mult timp comparând mai multe baze de date operaționale pentru a o selecta pe cea care se potrivește cel mai bine volumului de lucru prevăzut. Nevoile pot include modelarea simplificată a datelor, garanții tranzacționale, performanță de citire/scriere, scalare orizontală și toleranță la erori. În mod tradițional, alegerea începe cu categoria bazei de date, SQL sau NoSQL, deoarece fiecare categorie prezintă un set clar de compromisuri. Performanța înaltă în ceea ce privește latența scăzută și debitul mare este în general văzută ca o cerință fără compromis și, prin urmare, este esențială pentru orice bază de date eșantion.

Scopul acestui articol este de a ajuta dezvoltatorii de aplicații să facă alegerea corectă între SQL și NoSQL în contextul modelării datelor aplicației. Vom analiza o bază de date SQL, și anume PostgreSQL, și două baze de date NoSQL, Cassandra și MongoDB, pentru a acoperi elementele de bază ale designului bazei de date, cum ar fi crearea de tabele, popularea acestora, citirea datelor dintr-un tabel și ștergerea acestuia. În următorul articol, ne vom asigura că ne uităm la indici, tranzacții, JOIN-uri, directive TTL și design de baze de date bazat pe JSON.

Care este diferența dintre SQL și NoSQL?

Bazele de date SQL măresc flexibilitatea aplicațiilor prin garanțiile tranzacționale ACID, precum și capacitatea lor de a interoga datele folosind JOIN-uri în moduri neașteptate, pe lângă modelele de baze de date relaționale existente.

Având în vedere arhitectura lor monolitică/cu un singur nod și utilizarea unui model de replicare master-slave pentru redundanță, bazele de date tradiționale SQL nu au două caracteristici importante - scalabilitatea liniară a scrierii (adică partiționarea automată pe mai multe noduri) și pierderea automată/zero de date. Aceasta înseamnă că cantitatea de date primite nu poate depăși viteza maximă de scriere a unui singur nod. În plus, unele pierderi temporare de date trebuie luate în considerare în toleranța la erori (într-o arhitectură shared-nothing). Aici trebuie să rețineți că comiterile recente nu au fost încă reflectate în copia slave. Actualizările non-time sunt, de asemenea, dificil de realizat în bazele de date SQL.

Bazele de date NoSQL sunt de obicei distribuite prin natură, adică. în ele, datele sunt împărțite în secțiuni și distribuite pe mai multe noduri. Ele necesită denormalizare. Aceasta înseamnă că datele introduse trebuie și ele copiate de mai multe ori pentru a răspunde solicitărilor specifice pe care le trimiteți. Scopul general este de a obține performanțe ridicate prin reducerea numărului de fragmente disponibile în timpul citirilor. Acest lucru implică faptul că NoSQL vă cere să vă modelați interogările, în timp ce SQL vă cere să vă modelați datele.

NoSQL se concentrează pe obținerea de performanțe ridicate într-un cluster distribuit și acesta este rațiunea de bază pentru multe compromisuri de proiectare a bazelor de date care includ pierderea tranzacțiilor ACID, JOIN-uri și indici secundari globali consecvenți.

Există un argument că, în timp ce bazele de date NoSQL oferă scalabilitate liniară de scriere și toleranță ridicată la erori, pierderea garanțiilor tranzacționale le face nepotrivite pentru datele esențiale.

Următorul tabel arată modul în care modelarea datelor în NoSQL diferă de SQL.

Fundamentele de proiectare a bazelor de date - Compararea PostgreSQL, Cassandra și MongoDB

SQL și NoSQL: de ce sunt necesare ambele?

Aplicațiile din lumea reală cu un număr mare de utilizatori, cum ar fi Amazon.com, Netflix, Uber și Airbnb, au sarcina de a efectua sarcini complexe, cu mai multe fațete. De exemplu, o aplicație de comerț electronic precum Amazon.com trebuie să stocheze date ușoare, extrem de critice, cum ar fi informații despre utilizatori, produse, comenzi, facturi, împreună cu date grele și mai puțin sensibile, cum ar fi recenzii despre produse, mesaje de asistență, activitatea utilizatorului, recenzii și recomandări ale utilizatorilor. Desigur, aceste aplicații se bazează pe cel puțin o bază de date SQL împreună cu cel puțin o bază de date NoSQL. În sistemele interregionale și globale, o bază de date NoSQL funcționează ca un cache geo-distribuit pentru datele stocate într-o bază de date SQL sursă de încredere care rulează într-o singură regiune.

Cum combina YugaByte DB SQL și NoSQL?

Construit pe un motor de stocare mixt orientat spre jurnal, auto-sharding, replicare sharded distribuită consens și tranzacții distribuite ACID (inspirat de Google Spanner), YugaByte DB este prima bază de date open source din lume care este simultan compatibilă cu NoSQL (Cassandra & Redis) și SQL (PostgreSQL). După cum se arată în tabelul de mai jos, YCQL, YugaByte DB API compatibil cu Cassandra, adaugă conceptele de tranzacții ACID cu o singură cheie și mai multe chei și indici secundari globali la API-ul NoSQL, inaugurând astfel era bazelor de date tranzacționale NoSQL. În plus, YCQL, YugaByte DB API compatibil cu PostgreSQL, adaugă conceptele de scalare liniară a scrierii și toleranță automată la erori la API-ul SQL, aducând bazele de date SQL distribuite în lume. Deoarece YugaByte DB este de natură tranzacțională, API-ul NoSQL poate fi utilizat acum în contextul datelor critice pentru misiune.

Fundamentele de proiectare a bazelor de date - Compararea PostgreSQL, Cassandra și MongoDB

După cum s-a menționat anterior în articol „Prezentarea YSQL: un API SQL distribuit compatibil PostgreSQL pentru YugaByte DB”, alegerea între SQL sau NoSQL în YugaByte DB depinde în întregime de caracteristicile sarcinii de lucru subiacente:

  • Dacă sarcina dvs. principală de lucru este operațiunile JOIN cu mai multe chei, atunci când alegeți YSQL, înțelegeți că cheile dvs. pot fi distribuite pe mai multe noduri, ceea ce duce la o latență mai mare și/sau un debit mai mic decât NoSQL.
  • În caz contrar, alegeți oricare dintre cele două API-uri NoSQL, ținând cont că veți obține performanțe mai bune ca urmare a interogărilor servite de la un nod la un moment dat. YugaByte DB poate servi ca o bază de date operațională unică pentru aplicații complexe din lumea reală care trebuie să gestioneze mai multe sarcini de lucru simultan.

Laboratorul de modelare a datelor din secțiunea următoare se bazează pe baze de date YugaByte DB compatibile cu PostgreSQL și Cassandra API, spre deosebire de bazele de date native. Această abordare subliniază ușurința interacțiunii cu două API-uri diferite (pe două porturi diferite) ale aceluiași cluster de baze de date, spre deosebire de utilizarea clusterelor complet independente a două baze de date diferite.
În secțiunile următoare, vom arunca o privire asupra laboratorului de modelare a datelor pentru a ilustra diferențele și unele dintre punctele comune ale bazelor de date acoperite.

Laborator de modelare a datelor

Instalarea bazei de date

Având în vedere accentul pus pe proiectarea modelului de date (mai degrabă decât arhitecturi complexe de implementare), vom instala baze de date în containere Docker pe mașina locală și apoi vom interacționa cu acestea folosind shell-urile de linie de comandă respective.

Baza de date YugaByte DB compatibilă PostgreSQL și 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

Acces la linia de comandă

Să ne conectăm la bazele de date folosind shell-ul liniei de comandă pentru API-urile corespunzătoare.

PostgreSQL

psql este un shell de linie de comandă pentru interacțiunea cu PostgreSQL. Pentru ușurință în utilizare, YugaByte DB vine cu psql chiar în folderul bin.

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

Cassandra

cqlsh este un shell de linie de comandă pentru interacțiunea cu Cassandra și bazele de date compatibile prin intermediul CQL (Cassandra Query Language). Pentru ușurință în utilizare, YugaByte DB vine cu cqlsh în catalog bin.
Rețineți că CQL a fost inspirat de SQL și are concepte similare de tabele, rânduri, coloane și indici. Cu toate acestea, ca limbaj NoSQL, adaugă un anumit set de limitări, dintre care majoritatea le vom acoperi și în alte articole.

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

MongoDB

Mongo este un shell de linie de comandă pentru interacțiunea cu MongoDB. Poate fi găsit în directorul bin al instalării MongoDB.

docker exec -it my-mongo bash 
cd bin
mongo

Creați un tabel

Acum putem interacționa cu baza de date pentru a efectua diverse operații folosind linia de comandă. Să începem prin a crea un tabel care stochează informații despre melodiile scrise de diferiți artiști. Aceste melodii pot face parte dintr-un album. De asemenea, atributele opționale pentru o melodie sunt anul lansării, prețul, genul și ratingul. Trebuie să luăm în considerare atributele suplimentare care ar putea fi necesare în viitor prin câmpul „etichete”. Poate stoca date semi-structurate sub formă de perechi cheie-valoare.

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

Crearea unui tabel în Cassandra este foarte asemănătoare cu PostgreSQL. Una dintre principalele diferențe este lipsa constrângerilor de integritate (de exemplu, NOT NULL), dar aceasta este responsabilitatea aplicației, nu a bazei de date NoSQL.. Cheia primară constă dintr-o cheie de partiție (coloana Artist din exemplul de mai jos) și un set de coloane de grupare (coloana SongTitle din exemplul de mai jos). Cheia de partiție determină în ce partiție/shard trebuie plasat rândul, iar coloanele de grupare indică modul în care datele ar trebui să fie organizate în fragmentul curent.

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 organizează datele în baze de date (Baza de date) (similar cu Keyspace în Cassandra), unde există Colecții (asemănătoare cu tabelele) care conțin Documente (asemănătoare cu rândurile dintr-un tabel). În MongoDB, practic nu este nevoie să definiți o schemă inițială. Echipă "utilizați baza de date", prezentat mai jos, instanțiază baza de date la primul apel și modifică contextul pentru noua bază de date creată. Nici măcar colecțiile nu trebuie să fie create în mod explicit; ele sunt create automat, pur și simplu când adăugați primul document la o nouă colecție. Rețineți că MongoDB folosește baza de date de testare în mod implicit, astfel încât orice operațiune la nivel de colecție fără a specifica o anumită bază de date va rula în mod implicit pe ea.

use myNewDatabase;

Obținerea de informații despre un tabel
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;

Introducerea datelor într-un tabel
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

Expresie generală INSERT în Cassandra arată foarte asemănător cu cel din PostgreSQL. Cu toate acestea, există o mare diferență în semantică. În Cassandra INSERT este de fapt o operațiune UPSERT, unde ultimele valori sunt adăugate la rând dacă rândul există deja.

Introducerea datelor este similară cu PostgreSQL INSERT mai sus

.

MongoDB

Chiar dacă MongoDB este o bază de date NoSQL ca Cassandra, operația sa de inserare nu are nimic în comun cu comportamentul semantic al lui Cassandra. În MongoDB introduce() nu are oportunități UPSERT, ceea ce îl face similar cu PostgreSQL. Adăugarea datelor implicite fără _idspecified va face ca un nou document să fie adăugat la colecție.

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"
}
}
);

Interogare la tabel

Poate cea mai semnificativă diferență dintre SQL și NoSQL în ceea ce privește construcția interogărilor este limbajul folosit FROM и WHERE. SQL permite după exprimare FROM selectează mai multe tabele și expresie cu WHERE poate fi de orice complexitate (inclusiv operațiuni JOIN între mese). Cu toate acestea, NoSQL tinde să impună o limitare severă asupra FROM, și lucrează numai cu un tabel specificat și în WHERE, cheia primară trebuie întotdeauna specificată. Acest lucru se leagă de impulsul de performanță NoSQL despre care am vorbit mai devreme. Această dorință duce la orice reducere posibilă a oricărei interacțiuni încrucișate și încrucișate. Poate introduce o întârziere mare în comunicarea între noduri atunci când răspunde la o solicitare și, prin urmare, este cel mai bine evitată în general. De exemplu, Cassandra cere ca interogările să fie limitate la anumiți operatori (numai =, IN, <, >, =>, <=) pe cheile de partiție, cu excepția interogării unui index secundar (numai operatorul = este permis aici).

PostgreSQL

Mai jos sunt trei exemple de interogări care pot fi executate cu ușurință de o bază de date SQL.

  • Afișează toate melodiile unui artist;
  • Afișează toate melodiile artistului care se potrivesc cu prima parte a titlului;
  • Afișează toate melodiile unui artist care au un anumit cuvânt în titlu și au un preț mai mic de 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

Dintre interogările PostgreSQL enumerate mai sus, doar prima va funcționa neschimbată în Cassandra, deoarece operatorul LIKE nu poate fi aplicat la gruparea coloanelor, cum ar fi SongTitle. În acest caz, sunt permise numai operatorii = и 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

După cum se arată în exemplele anterioare, metoda principală pentru crearea de interogări în MongoDB este db.collection.find(). Această metodă conține în mod explicit numele colecției (music în exemplul de mai jos), deci interogarea mai multor colecții este interzisă.

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

Citirea tuturor rândurilor unui tabel

Citirea tuturor rândurilor este pur și simplu un caz special al modelului de interogare la care ne-am uitat mai devreme.

PostgreSQL

SELECT * 
FROM Music;

Cassandra

Similar cu exemplul PostgreSQL de mai sus.

MongoDB

db.music.find( {} );

Editarea datelor într-un tabel

PostgreSQL

PostgreSQL oferă instrucțiuni UPDATE pentru a schimba datele. Ea nu are oportunități UPSERT, deci această declarație va eșua dacă rândul nu se mai află în baza de date.

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

Cassandra

Cassandra are UPDATE similar cu PostgreSQL. UPDATE are aceeași semantică UPSERT, asemănător INSERT.

Similar cu exemplul PostgreSQL de mai sus.

MongoDB
Operație Actualizați() în MongoDB poate actualiza complet un document existent sau actualiza doar anumite câmpuri. În mod implicit, actualizează doar un document cu semantica dezactivată UPSERT. Actualizarea mai multor documente și comportament similar UPSERT poate fi aplicat prin setarea de steaguri suplimentare pentru operație. De exemplu, în exemplul de mai jos, genul unui anumit artist este actualizat în funcție de cântecul său.

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

Eliminarea datelor dintr-un tabel

PostgreSQL

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

Cassandra

Similar cu exemplul PostgreSQL de mai sus.

MongoDB

MongoDB are două tipuri de operații pentru ștergerea documentelor − deleteOne() /deleteMany() и elimina(). Ambele tipuri șterg documentele, dar returnează rezultate diferite.

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

Ștergerea unui tabel

PostgreSQL

DROP TABLE Music;

Cassandra

Similar cu exemplul PostgreSQL de mai sus.

MongoDB

db.music.drop();

Concluzie

Dezbaterea despre alegerea între SQL și NoSQL face furori de mai bine de 10 ani. Există două aspecte principale ale acestei dezbateri: arhitectura motorului de baze de date (monolitic, SQL tranzacțional vs distribuit, NoSQL non-tranzacțional) și abordarea de proiectare a bazei de date (modelarea datelor în SQL vs modelarea interogărilor în NoSQL).

Cu o bază de date tranzacțională distribuită precum YugaByte DB, dezbaterea despre arhitectura bazei de date poate fi ușor oprită. Pe măsură ce volumele de date devin mai mari decât ceea ce poate fi scris pe un singur nod, devine necesară o arhitectură complet distribuită care să accepte scalabilitate liniară de scriere cu fragmentare/reechilibrare automată.

În plus, așa cum se spune într-unul dintre articole Google CloudArhitecturile tranzacționale, puternic consistente, sunt acum mai folosite pentru a oferi o mai bună agilitate de dezvoltare decât arhitecturile netranzacționale, eventual consecvente.

Revenind la discuția despre proiectarea bazei de date, este corect să spunem că ambele abordări de proiectare (SQL și NoSQL) sunt necesare pentru orice aplicație complexă din lumea reală. Abordarea de „modelare a datelor” SQL permite dezvoltatorilor să îndeplinească mai ușor cerințele de afaceri în schimbare, în timp ce abordarea de „modelare a interogărilor” NoSQL permite acelorași dezvoltatori să opereze pe volume mari de date cu latență scăzută și debit mare. Din acest motiv, YugaByte DB oferă API-uri SQL și NoSQL într-un nucleu comun, mai degrabă decât să promoveze una dintre abordări. În plus, oferind compatibilitate cu limbaje populare de baze de date, inclusiv PostgreSQL și Cassandra, YugaByte DB se asigură că dezvoltatorii nu trebuie să învețe o altă limbă pentru a lucra cu un motor de baze de date distribuit, foarte consistent.

În acest articol, am analizat cum diferă elementele fundamentale ale designului bazei de date între PostgreSQL, Cassandra și MongoDB. În articolele viitoare, ne vom scufunda în concepte avansate de design, cum ar fi indici, tranzacții, JOIN-uri, directive TTL și documente JSON.

Vă dorim un rest minunat de weekend și vă invităm webinar gratuit, care va avea loc pe 14 mai.

Sursa: www.habr.com

Adauga un comentariu