Database Design Fundamentals - Sammenligning av PostgreSQL, Cassandra og MongoDB

Hei venner. Før avreise til andre del av maiferien deler vi med deg materialet som vi oversatte i påvente av lanseringen av en ny strøm på kurset "Relasjonell DBMS".

Database Design Fundamentals - Sammenligning av PostgreSQL, Cassandra og MongoDB

Applikasjonsutviklere bruker mye tid på å sammenligne flere operasjonelle databaser for å velge den som passer best til den tiltenkte arbeidsmengden. Behov kan omfatte forenklet datamodellering, transaksjonsgarantier, lese-/skriveytelse, horisontal skalering og feiltoleranse. Tradisjonelt begynner valget med databasekategorien, SQL eller NoSQL, siden hver kategori presenterer et klart sett med avveininger. Høy ytelse når det gjelder lav ventetid og høy gjennomstrømning blir generelt sett på som et ikke-avveiningskrav og er derfor avgjørende for enhver prøvedatabase.

Hensikten med denne artikkelen er å hjelpe applikasjonsutviklere til å ta det riktige valget mellom SQL og NoSQL i sammenheng med applikasjonsdatamodellering. Vi skal se på én SQL-database, nemlig PostgreSQL, og to NoSQL-databaser, Cassandra og MongoDB, for å dekke det grunnleggende om databasedesign, for eksempel å lage tabeller, fylle dem, lese data fra en tabell og slette den. I den neste artikkelen skal vi se på indekser, transaksjoner, JOINs, TTL-direktiver og JSON-basert databasedesign.

Hva er forskjellen mellom SQL og NoSQL?

SQL-databaser øker applikasjonsfleksibiliteten gjennom ACID-transaksjonsgarantier, samt deres evne til å søke etter data ved hjelp av JOIN-er på uventede måter på toppen av eksisterende normaliserte relasjonsdatabasemodeller.

Gitt deres monolittiske/single-node-arkitektur og bruk av en master-slave-replikeringsmodell for redundans, mangler tradisjonelle SQL-databaser to viktige funksjoner - lineær skriveskalerbarhet (dvs. automatisk partisjonering over flere noder) og automatisk/null datatap. Dette betyr at mengden data som mottas ikke kan overstige maksimal skrivegjennomstrømning for en enkelt node. I tillegg må noe midlertidig datatap tas i betraktning i feiltoleranse (i en delt-ingenting-arkitektur). Her må du huske på at nylige forpliktelser ennå ikke har blitt reflektert i slavekopien. Oppdateringer uten nedetid er også vanskelig å oppnå i SQL-databaser.

NoSQL-databaser er vanligvis distribuert av natur, dvs. i dem er data delt inn i seksjoner og fordelt på flere noder. De krever denormalisering. Dette betyr at dataene som legges inn også må kopieres flere ganger for å svare på de spesifikke forespørslene du sender. Det overordnede målet er å oppnå høy ytelse ved å redusere antall tilgjengelige shards under lesing. Dette innebærer at NoSQL krever at du modellerer spørringene dine, mens SQL krever at du modellerer dataene dine.

NoSQL fokuserer på å oppnå høy ytelse i en distribuert klynge, og dette er den underliggende begrunnelsen for mange avveininger i databasedesign som inkluderer tap av ACID-transaksjoner, JOINs og konsistente globale sekundære indekser.

Det er et argument at mens NoSQL-databaser gir lineær skriveskalerbarhet og høy feiltoleranse, gjør tapet av transaksjonsgarantier dem uegnet for virksomhetskritiske data.

Tabellen nedenfor viser hvordan datamodellering i NoSQL skiller seg fra SQL.

Database Design Fundamentals - Sammenligning av PostgreSQL, Cassandra og MongoDB

SQL og NoSQL: Hvorfor trengs begge?

Virkelige applikasjoner med et stort antall brukere, som Amazon.com, Netflix, Uber og Airbnb, har i oppgave å utføre komplekse, mangefasetterte oppgaver. For eksempel må en e-handelsapplikasjon som Amazon.com lagre lette, høykritiske data som brukerinformasjon, produkter, bestillinger, fakturaer, sammen med tunge, mindre sensitive data som produktanmeldelser, støttemeldinger, brukeraktivitet, brukeranmeldelser og anbefalinger. Naturligvis er disse applikasjonene avhengige av minst én SQL-database sammen med minst én NoSQL-database. I tverrregionale og globale systemer fungerer en NoSQL-database som en geo-distribuert hurtigbuffer for data lagret i en pålitelig kilde-SQL-database som kjører i en enkelt region.

Hvordan kombinerer YugaByte DB SQL og NoSQL?

YugaByte DB er bygget på en loggorientert blandet lagringsmotor, auto-sharding, sharded distribuert konsensusreplikering og ACID-distribuerte transaksjoner (inspirert av Google Spanner), og er verdens første åpen kildekode-database som samtidig er kompatibel med NoSQL (Cassandra & Redis ) og SQL (PostgreSQL). Som vist i tabellen nedenfor, legger YCQL, YugaByte DB API kompatibel med Cassandra, konseptene med enkelt- og flernøkkel ACID-transaksjoner og globale sekundære indekser til NoSQL API, og innleder dermed æraen med transaksjonelle NoSQL-databaser. YCQL, YugaByte DB API som er kompatibel med PostgreSQL, legger i tillegg konseptene lineær skriveskalering og automatisk feiltoleranse til SQL API, og bringer distribuerte SQL-databaser til verden. Fordi YugaByte DB er transaksjonell i naturen, kan NoSQL API nå brukes i sammenheng med oppdragskritiske data.

Database Design Fundamentals - Sammenligning av PostgreSQL, Cassandra og MongoDB

Som tidligere nevnt i artikkelen "Vi introduserer YSQL: En PostgreSQL-kompatibel distribuert SQL API for YugaByte DB", valget mellom SQL eller NoSQL i YugaByte DB avhenger helt av egenskapene til den underliggende arbeidsbelastningen:

  • Hvis den primære arbeidsbelastningen din er JOIN-operasjoner med flere nøkler, må du forstå at nøklene dine kan være fordelt på flere noder når du velger YSQL, noe som resulterer i høyere ventetid og/eller lavere gjennomstrømning enn NoSQL.
  • Ellers velger du en av de to NoSQL API-ene, og husk at du vil få bedre ytelse som et resultat av spørringer servert fra en node om gangen. YugaByte DB kan fungere som en enkelt operasjonell database for komplekse applikasjoner i den virkelige verden som må håndtere flere arbeidsbelastninger samtidig.

Datamodelleringslaben i neste seksjon er basert på PostgreSQL og Cassandra API-kompatible YugaByte DB-databaser, i motsetning til native databaser. Denne tilnærmingen understreker det enkle å samhandle med to forskjellige APIer (på to forskjellige porter) i samme databaseklynge, i motsetning til å bruke helt uavhengige klynger av to forskjellige databaser.
I de følgende delene tar vi en titt på datamodelleringslaben for å illustrere forskjellene og noen av fellestrekkene til databasene som dekkes.

Datamodelleringslaboratorium

Database installasjon

Gitt vekten på datamodelldesign (i stedet for komplekse distribusjonsarkitekturer), vil vi installere databaser i Docker-beholdere på den lokale maskinen og deretter samhandle med dem ved å bruke deres respektive kommandolinjeskall.

PostgreSQL & Cassandra-kompatibel YugaByte DB-database

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

Kommandolinjetilgang

La oss koble til databasene ved å bruke kommandolinjeskallet for de tilsvarende APIene.

PostgreSQL

psql er et kommandolinjeskall for samhandling med PostgreSQL. For enkel bruk kommer YugaByte DB med psql rett i bin-mappen.

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

Cassandra

cqlsh er et kommandolinjeskall for samhandling med Cassandra og dens kompatible databaser via CQL (Cassandra Query Language). For enkel bruk kommer YugaByte DB med cqlsh i katalogen bin.
Merk at CQL ble inspirert av SQL og har lignende konsepter for tabeller, rader, kolonner og indekser. Som et NoSQL-språk legger det imidlertid til et visst sett med begrensninger, de fleste av dem vil vi også dekke i andre artikler.

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

MongoDB

mongo er et kommandolinjeskall for samhandling med MongoDB. Den finnes i bin-katalogen til MongoDB-installasjonen.

docker exec -it my-mongo bash 
cd bin
mongo

Lag en tabell

Nå kan vi samhandle med databasen for å utføre ulike operasjoner ved hjelp av kommandolinjen. La oss starte med å lage en tabell som lagrer informasjon om sanger skrevet av forskjellige artister. Disse sangene kan være en del av et album. Valgfrie attributter for en sang er også utgivelsesår, pris, sjanger og vurdering. Vi må gjøre rede for ytterligere attributter som kan være nødvendig i fremtiden gjennom "tags"-feltet. Den kan lagre semistrukturerte data i form av nøkkelverdi-par.

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

Å lage en tabell i Cassandra er veldig lik PostgreSQL. En av hovedforskjellene er mangelen på integritetsbegrensninger (f.eks. IKKE NULL), men dette er applikasjonens ansvar, ikke NoSQL-databasen. Primærnøkkelen består av en partisjonsnøkkel (Artist-kolonnen i eksemplet nedenfor) og et sett med klyngekolonner (SongTitle-kolonnen i eksemplet nedenfor). Partisjonsnøkkelen bestemmer hvilken partisjon/shard raden skal plasseres i, og klyngekolonnene angir hvordan dataene skal organiseres innenfor gjeldende shard.

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 organiserer data i databaser (Database) (ligner Keyspace i Cassandra), der det er samlinger (ligner på tabeller) som inneholder Dokumenter (ligner rader i en tabell). I MongoDB er det i utgangspunktet ikke nødvendig å definere et innledende skjema. Team "bruk database", vist nedenfor, instansierer databasen ved det første anropet og endrer konteksten for den nyopprettede databasen. Selv samlinger trenger ikke å opprettes eksplisitt; de opprettes automatisk, ganske enkelt når du legger til det første dokumentet i en ny samling. Merk at MongoDB bruker testdatabasen som standard, så enhver operasjon på samlingsnivå uten å spesifisere en spesifikk database vil kjøre på den som standard.

use myNewDatabase;

Få informasjon om en tabell
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;

Legge inn data i en tabell
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

Helhetsuttrykk INSERT i Cassandra ser veldig ut som det i PostgreSQL. Det er imidlertid én stor forskjell i semantikk. I Cassandra INSERT er faktisk en operasjon UPSERT, hvor de siste verdiene legges til raden hvis raden allerede eksisterer.

Dataregistrering ligner på PostgreSQL INSERT ovenfor

.

MongoDB

Selv om MongoDB er en NoSQL-database som Cassandra, har innsettingsoperasjonen ingenting til felles med den semantiske oppførselen til Cassandra. I MongoDB sett inn() har ingen muligheter UPSERT, som gjør den lik PostgreSQL. Legger til standarddata uten _idspecified vil føre til at et nytt dokument legges til samlingen.

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

Tabellspørring

Den kanskje viktigste forskjellen mellom SQL og NoSQL når det gjelder spørringskonstruksjon er språket som brukes FROM и WHERE. SQL tillater etter uttrykk FROM velg flere tabeller, og uttrykk med WHERE kan være av hvilken som helst kompleksitet (inkludert operasjoner JOIN mellom bordene). NoSQL har imidlertid en tendens til å pålegge en alvorlig begrensning på FROM, og fungerer bare med én spesifisert tabell, og i WHERE, må primærnøkkelen alltid spesifiseres. Dette knytter seg til NoSQL-ytelsespushet vi snakket om tidligere. Dette ønsket fører til enhver mulig reduksjon i enhver interaksjon på tvers av tabeller og nøkkelnøkler. Det kan introdusere en stor forsinkelse i kommunikasjon mellom noder når du svarer på en forespørsel, og er derfor best å unngå generelt. For eksempel krever Cassandra at forespørsler er begrenset til visse operatører (kun =, IN, <, >, =>, <=) på partisjonsnøkler, bortsett fra når du ber om en sekundær indeks (bare =-operatøren er tillatt her).

PostgreSQL

Nedenfor er tre eksempler på spørringer som enkelt kan utføres av en SQL-database.

  • Vis alle sanger av en artist;
  • Vis alle sangene av artisten som samsvarer med den første delen av tittelen;
  • Vis alle sanger av en artist som har et bestemt ord i tittelen og har en pris under 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

Av PostgreSQL-spørringene som er oppført ovenfor, vil bare den første fungere uendret i Cassandra, siden operatøren LIKE kan ikke brukes på klyngekolonner som f.eks SongTitle. I dette tilfellet er kun operatører tillatt = и 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

Som vist i de foregående eksemplene, er hovedmetoden for å lage spørringer i MongoDB db.collection.find(). Denne metoden inneholder eksplisitt navnet på samlingen (music i eksemplet nedenfor), så det er forbudt å søke etter flere samlinger.

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

Leser alle rader i en tabell

Å lese alle rader er ganske enkelt et spesialtilfelle av spørringsmønsteret vi så på tidligere.

PostgreSQL

SELECT * 
FROM Music;

Cassandra

Ligner på PostgreSQL-eksemplet ovenfor.

MongoDB

db.music.find( {} );

Redigering av data i en tabell

PostgreSQL

PostgreSQL gir instruksjoner UPDATE å endre data. Hun har ingen muligheter UPSERT, så denne setningen vil mislykkes hvis raden ikke lenger er i databasen.

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

Cassandra

Cassandra har UPDATE ligner på PostgreSQL. UPDATE har samme semantikk UPSERT, lignende INSERT.

Ligner på PostgreSQL-eksemplet ovenfor.

MongoDB
Operasjon Oppdater() i MongoDB kan fullstendig oppdatere et eksisterende dokument eller oppdatere bare visse felt. Som standard oppdaterer den bare ett dokument med semantikk deaktivert UPSERT. Oppdatering av flere dokumenter og lignende oppførsel UPSERT kan brukes ved å sette flere flagg for operasjonen. For eksempel, i eksemplet nedenfor, oppdateres sjangeren til en spesifikk artist basert på sangen hans.

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

Fjerne data fra en tabell

PostgreSQL

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

Cassandra

Ligner på PostgreSQL-eksemplet ovenfor.

MongoDB

MongoDB har to typer operasjoner for å slette dokumenter − deleteOne() /deleteMany() и ta vekk(). Begge typer sletter dokumenter, men gir forskjellige resultater.

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

Sletter en tabell

PostgreSQL

DROP TABLE Music;

Cassandra

Ligner på PostgreSQL-eksemplet ovenfor.

MongoDB

db.music.drop();

Konklusjon

Debatten om å velge mellom SQL og NoSQL har pågått i mer enn 10 år. Det er to hovedaspekter ved denne debatten: databasemotorarkitektur (monolitisk, transaksjonell SQL vs distribuert, ikke-transaksjonell NoSQL) og databasedesigntilnærming (modellering av dataene dine i SQL vs modellering av spørringene dine i NoSQL).

Med en distribuert transaksjonsdatabase som YugaByte DB, kan debatten om databasearkitektur lett legges til ro. Ettersom datavolumer blir større enn det som kan skrives til en enkelt node, blir det nødvendig med en fullt distribuert arkitektur som støtter lineær skriveskalerbarhet med automatisk sharding/rebalansering.

Dessuten, som det står i en av artiklene Google Cloud,Transaksjonelle, sterkt konsistente arkitekturer brukes nå mer for å gi bedre utviklingssmidighet enn ikke-transaksjonelle, til slutt konsistente arkitekturer.

Når vi kommer tilbake til diskusjonen om databasedesign, er det rimelig å si at begge designtilnærmingene (SQL og NoSQL) er nødvendige for enhver kompleks applikasjon i den virkelige verden. SQL "datamodellering"-tilnærmingen lar utviklere lettere møte endrede forretningskrav, mens NoSQL "query modeling"-tilnærmingen lar de samme utviklerne operere på store datavolumer med lav latens og høy gjennomstrømning. Det er av denne grunn at YugaByte DB tilbyr SQL og NoSQL APIer i en felles kjerne, i stedet for å fremme en av tilnærmingene. I tillegg, ved å tilby kompatibilitet med populære databasespråk, inkludert PostgreSQL og Cassandra, sikrer YugaByte DB at utviklere ikke trenger å lære et annet språk for å jobbe med en distribuert, svært konsistent databasemotor.

I denne artikkelen så vi på hvordan grunnleggende databasedesign skiller seg mellom PostgreSQL, Cassandra og MongoDB. I fremtidige artikler vil vi dykke ned i avanserte designkonsepter som indekser, transaksjoner, JOINs, TTL-direktiver og JSON-dokumenter.

Vi ønsker deg en fin helg og inviterer deg til gratis webinar, som finner sted 14. mai.

Kilde: www.habr.com

Legg til en kommentar