Basisprincipes van databaseontwerp - Vergelijking van PostgreSQL, Cassandra en MongoDB

Hallo vrienden. Voordat we naar het tweede deel van de meivakantie vertrekken, delen we met u het materiaal dat we hebben vertaald in afwachting van de lancering van een nieuwe stream op de cursus "Relationele DBMS".

Basisprincipes van databaseontwerp - Vergelijking van PostgreSQL, Cassandra en MongoDB

Applicatieontwikkelaars besteden veel tijd aan het vergelijken van meerdere operationele databases om de database te selecteren die het beste past bij de beoogde werklast. Behoeften kunnen bestaan ​​uit vereenvoudigde datamodellering, transactionele garanties, lees-/schrijfprestaties, horizontale schaling en fouttolerantie. Traditioneel begint de keuze met de databasecategorie, SQL of NoSQL, omdat elke categorie een duidelijke reeks afwegingen met zich meebrengt. Hoge prestaties in termen van lage latentie en hoge doorvoer worden over het algemeen gezien als een niet-traditionele vereiste en zijn daarom essentieel voor elke voorbeelddatabase.

Het doel van dit artikel is om applicatieontwikkelaars te helpen de juiste keuze te maken tussen SQL en NoSQL in de context van applicatiedatamodellering. We zullen kijken naar één SQL-database, namelijk PostgreSQL, en twee NoSQL-databases, Cassandra en MongoDB, om de basisprincipes van databaseontwerp te behandelen, zoals het maken van tabellen, het vullen ervan, het lezen van gegevens uit een tabel en het verwijderen ervan. In het volgende artikel zullen we zeker kijken naar indexen, transacties, JOIN's, TTL-richtlijnen en op JSON gebaseerd databaseontwerp.

Wat is het verschil tussen SQL en NoSQL?

SQL-databases vergroten de applicatieflexibiliteit door middel van ACID-transactiegaranties, evenals hun vermogen om gegevens op te vragen met behulp van JOIN's op onverwachte manieren bovenop bestaande genormaliseerde relationele databasemodellen.

Gegeven hun monolithische architectuur met één knooppunt en het gebruik van een master-slave-replicatiemodel voor redundantie, missen traditionele SQL-databases twee belangrijke kenmerken: lineaire schrijfschaalbaarheid (d.w.z. automatische partitie over meerdere knooppunten) en automatisch/geen gegevensverlies. Dit betekent dat de hoeveelheid ontvangen gegevens de maximale schrijfdoorvoer van een enkel knooppunt niet kan overschrijden. Bovendien moet bij de fouttolerantie (in een architectuur waarbij niets wordt gedeeld) rekening worden gehouden met enig tijdelijk gegevensverlies. Hier moet je in gedachten houden dat recente commits nog niet zijn weerspiegeld in de slave-kopie. Ook in SQL-databases zijn niet-downtime-updates moeilijk te realiseren.

NoSQL-databases worden meestal van nature gedistribueerd, d.w.z. daarin worden gegevens verdeeld in secties en verdeeld over verschillende knooppunten. Ze vereisen denormalisatie. Dit betekent dat de ingevoerde gegevens ook meerdere keren moeten worden gekopieerd om te kunnen reageren op de specifieke verzoeken die u verzendt. Het algemene doel is om hoge prestaties te verkrijgen door het aantal shards dat beschikbaar is tijdens het lezen te verminderen. Dit houdt in dat NoSQL vereist dat u uw query's modelleert, terwijl SQL vereist dat u uw gegevens modelleert.

NoSQL richt zich op het bereiken van hoge prestaties in een gedistribueerd cluster en dit is de onderliggende reden voor veel compromissen bij het ontwerpen van databases, waaronder ACID-transactieverlies, JOIN's en consistente wereldwijde secundaire indexen.

Er is een argument dat hoewel NoSQL-databases lineaire schrijfschaalbaarheid en hoge fouttolerantie bieden, het verlies van transactionele garanties ze ongeschikt maakt voor bedrijfskritische gegevens.

De volgende tabel laat zien hoe gegevensmodellering in NoSQL verschilt van SQL.

Basisprincipes van databaseontwerp - Vergelijking van PostgreSQL, Cassandra en MongoDB

SQL en NoSQL: waarom zijn beide nodig?

Real-world applicaties met grote aantallen gebruikers, zoals Amazon.com, Netflix, Uber en Airbnb, zijn belast met het uitvoeren van complexe, veelzijdige taken. Een e-commerce-applicatie als Amazon.com moet bijvoorbeeld lichtgewicht, zeer kritische gegevens opslaan, zoals gebruikersinformatie, producten, bestellingen, facturen, samen met zware, minder gevoelige gegevens zoals productrecensies, ondersteuningsberichten, gebruikersactiviteit, gebruikersrecensies en aanbevelingen. Uiteraard zijn deze applicaties afhankelijk van ten minste één SQL-database en ten minste één NoSQL-database. In interregionale en mondiale systemen functioneert een NoSQL-database als een geografisch gedistribueerde cache voor gegevens die zijn opgeslagen in een vertrouwde SQL-brondatabase die in een bepaalde regio wordt uitgevoerd.

Hoe combineert YugaByte DB SQL en NoSQL?

Gebouwd op een log-georiënteerde mixed storage-engine, auto-sharding, sharded gedistribueerde consensusreplicatie en ACID gedistribueerde transacties (geïnspireerd door Google Spanner), is YugaByte DB 's werelds eerste open source database die tegelijkertijd compatibel is met NoSQL (Cassandra & Redis) en SQL (PostgreSQL). Zoals weergegeven in de onderstaande tabel voegt YCQL, de YugaByte DB API die compatibel is met Cassandra, de concepten van ACID-transacties met één en meerdere sleutels en globale secundaire indexen toe aan de NoSQL API, waarmee het tijdperk van transactionele NoSQL-databases wordt ingeluid. Bovendien voegt YCQL, de YugaByte DB API die compatibel is met PostgreSQL, de concepten van lineaire schrijfschaling en automatische fouttolerantie toe aan de SQL API, waardoor gedistribueerde SQL-databases naar de wereld worden gebracht. Omdat YugaByte DB transactioneel van aard is, kan de NoSQL API nu worden gebruikt in de context van bedrijfskritische gegevens.

Basisprincipes van databaseontwerp - Vergelijking van PostgreSQL, Cassandra en MongoDB

Zoals eerder in het artikel vermeld "Introductie van YSQL: een PostgreSQL-compatibele gedistribueerde SQL-API voor YugaByte DB", hangt de keuze tussen SQL of NoSQL in YugaByte DB volledig af van de kenmerken van de onderliggende werklast:

  • Als uw primaire werklast bestaat uit JOIN-bewerkingen met meerdere sleutels, moet u er bij het kiezen van YSQL rekening mee houden dat uw sleutels over meerdere knooppunten kunnen worden verdeeld, wat resulteert in een hogere latentie en/of lagere doorvoer dan NoSQL.
  • Kies anders een van de twee NoSQL-API's, waarbij u er rekening mee moet houden dat u betere prestaties krijgt als gevolg van zoekopdrachten die vanaf één knooppunt tegelijk worden aangeboden. YugaByte DB kan dienen als een enkele operationele database voor real-world, complexe applicaties die meerdere workloads tegelijkertijd moeten beheren.

Het lab voor gegevensmodellering in de volgende sectie is gebaseerd op PostgreSQL- en Cassandra API-compatibele YugaByte DB-databases, in tegenstelling tot native databases. Deze aanpak benadrukt het gemak van interactie met twee verschillende API's (op twee verschillende poorten) van hetzelfde databasecluster, in tegenstelling tot het gebruik van volledig onafhankelijke clusters van twee verschillende databases.
In de volgende secties bekijken we het datamodelleringslab om de verschillen en enkele overeenkomsten tussen de behandelde databases te illustreren.

Laboratorium voor gegevensmodellering

Database-installatie

Gezien de nadruk op het ontwerp van datamodellen (in plaats van complexe implementatiearchitecturen), zullen we databases in Docker-containers op de lokale machine installeren en er vervolgens mee communiceren met behulp van hun respectievelijke opdrachtregelshells.

PostgreSQL & Cassandra-compatibele 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

Toegang via de opdrachtregel

Laten we verbinding maken met de databases met behulp van de opdrachtregelshell voor de bijbehorende API's.

PostgreSQL

psql is een opdrachtregelshell voor interactie met PostgreSQL. Voor gebruiksgemak wordt YugaByte DB geleverd met psql rechtstreeks in de bin-map.

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

Cassandra

cqlsh is een opdrachtregelshell voor interactie met Cassandra en de compatibele databases via CQL (Cassandra Query Language). Voor gebruiksgemak wordt YugaByte DB meegeleverd cqlsh in de catalogus bin.
Merk op dat CQL is geïnspireerd door SQL en vergelijkbare concepten heeft van tabellen, rijen, kolommen en indexen. Als NoSQL-taal voegt het echter een aantal beperkingen toe, waarvan we de meeste ook in andere artikelen zullen bespreken.

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

MongoDB

mongo is een opdrachtregelshell voor interactie met MongoDB. Het is te vinden in de bin-map van de MongoDB-installatie.

docker exec -it my-mongo bash 
cd bin
mongo

Een tabel maken

Nu kunnen we communiceren met de database om verschillende bewerkingen uit te voeren via de opdrachtregel. Laten we beginnen met het maken van een tabel waarin informatie wordt opgeslagen over nummers die door verschillende artiesten zijn geschreven. Deze nummers kunnen deel uitmaken van een album. Ook optionele attributen voor een nummer zijn het jaar van uitgave, prijs, genre en beoordeling. We moeten rekening houden met aanvullende attributen die in de toekomst mogelijk nodig zijn via het veld 'tags'. Het kan semi-gestructureerde gegevens opslaan in de vorm van sleutel-waardeparen.

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

Het maken van een tabel in Cassandra lijkt sterk op PostgreSQL. Een van de belangrijkste verschillen is het ontbreken van integriteitsbeperkingen (bijvoorbeeld NOT NULL), maar dit is de verantwoordelijkheid van de applicatie, niet van de NoSQL-database. De primaire sleutel bestaat uit een partitiesleutel (de kolom Artiest in het onderstaande voorbeeld) en een reeks clusterkolommen (de kolom SongTitle in het onderstaande voorbeeld). De partitiesleutel bepaalt in welke partitie/shard de rij moet worden geplaatst, en de clusterkolommen geven aan hoe de gegevens binnen de huidige shard moeten worden georganiseerd.

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 organiseert gegevens in databases (Database) (vergelijkbaar met Keyspace in Cassandra), waar er collecties zijn (vergelijkbaar met tabellen) die documenten bevatten (vergelijkbaar met rijen in een tabel). In MongoDB is het in principe niet nodig om een ​​initieel schema te definiëren. Team "gebruik databank", hieronder weergegeven, instantiëert de database bij de eerste aanroep en wijzigt de context voor de nieuw gemaakte database. Zelfs collecties hoeven niet expliciet te worden aangemaakt; ze worden automatisch aangemaakt, simpelweg wanneer u het eerste document aan een nieuwe collectie toevoegt. Houd er rekening mee dat MongoDB standaard de testdatabase gebruikt, dus elke bewerking op verzamelingsniveau zonder een specifieke database op te geven, wordt er standaard op uitgevoerd.

use myNewDatabase;

Informatie over een tafel opvragen
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;

Gegevens in een tabel invoeren
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

Algemene expressie INSERT in Cassandra lijkt erg op die in PostgreSQL. Er is echter één groot verschil in semantiek. In Cassandra INSERT is eigenlijk een operatie UPSERT, waarbij de laatste waarden aan de rij worden toegevoegd als de rij al bestaat.

Gegevensinvoer is vergelijkbaar met PostgreSQL INSERT boven

.

MongoDB

Ook al is MongoDB een NoSQL-database zoals Cassandra, de invoegbewerking heeft niets gemeen met het semantische gedrag van Cassandra. In MongoDB invoegen () heeft geen kansen UPSERT, waardoor het vergelijkbaar is met PostgreSQL. Standaardgegevens toevoegen zonder _idspecified zorgt ervoor dat er een nieuw document aan de collectie wordt toegevoegd.

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

Tabelquery

Misschien wel het belangrijkste verschil tussen SQL en NoSQL in termen van queryconstructie is de gebruikte taal FROM и WHERE. SQL maakt na-expressie mogelijk FROM selecteer meerdere tabellen en expressie met WHERE kan van elke complexiteit zijn (inclusief bewerkingen). JOIN tussen tafels). NoSQL heeft echter de neiging ernstige beperkingen op te leggen FROM, en werk alleen met één opgegeven tabel, en in WHERE, moet de primaire sleutel altijd worden opgegeven. Dit sluit aan bij de prestatieverbetering van NoSQL waar we het eerder over hadden. Deze wens leidt tot elke mogelijke vermindering van elke kruistabel- en kruissleutelinteractie. Het kan een grote vertraging veroorzaken in de communicatie tussen knooppunten bij het reageren op een verzoek en kan daarom in het algemeen het beste worden vermeden. Cassandra vereist bijvoorbeeld dat zoekopdrachten beperkt blijven tot bepaalde operators (alleen =, IN, <, >, =>, <=) op partitiesleutels, behalve bij het aanvragen van een secundaire index (hier is alleen de operator = toegestaan).

PostgreSQL

Hieronder staan ​​drie voorbeelden van queries die eenvoudig door een SQL-database kunnen worden uitgevoerd.

  • Toon alle nummers van een artiest;
  • Toon alle nummers van de artiest die overeenkomen met het eerste deel van de titel;
  • Toon alle nummers van een artiest met een bepaald woord in de titel en een prijs lager dan 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

Van de hierboven genoemde PostgreSQL-query's zal alleen de eerste onveranderd werken in Cassandra, aangezien de operator LIKE kan niet worden toegepast op clusterkolommen zoals SongTitle. In dit geval zijn alleen operators toegestaan = и 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

Zoals u in de vorige voorbeelden kunt zien, is de belangrijkste methode voor het maken van query's in MongoDB db.collectie.find(). Deze methode bevat expliciet de naam van de verzameling (music in het onderstaande voorbeeld), dus het bevragen van meerdere collecties is niet toegestaan.

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

Alle rijen van een tabel lezen

Het lezen van alle rijen is eenvoudigweg een speciaal geval van het zoekpatroon waar we eerder naar keken.

PostgreSQL

SELECT * 
FROM Music;

Cassandra

Vergelijkbaar met het PostgreSQL-voorbeeld hierboven.

MongoDB

db.music.find( {} );

Gegevens in een tabel bewerken

PostgreSQL

PostgreSQL biedt instructies UPDATE gegevens wijzigen. Ze heeft geen kansen UPSERT, dus deze instructie mislukt als de rij niet langer in de database staat.

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

Cassandra

Cassandra heeft UPDATE gelijk aan PostgreSQL UPDATE heeft dezelfde semantiek UPSERT, leuk vinden INSERT.

Vergelijkbaar met het PostgreSQL-voorbeeld hierboven.

MongoDB
Operatie bijwerken() in MongoDB kan een bestaand document volledig bijwerken of alleen bepaalde velden bijwerken. Standaard wordt slechts één document bijgewerkt terwijl de semantiek is uitgeschakeld UPSERT. Meerdere documenten bijwerken en soortgelijk gedrag UPSERT kan worden toegepast door extra vlaggen voor de bewerking in te stellen. In het onderstaande voorbeeld wordt bijvoorbeeld het genre van een specifieke artiest bijgewerkt op basis van zijn nummer.

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

Gegevens uit een tabel verwijderen

PostgreSQL

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

Cassandra

Vergelijkbaar met het PostgreSQL-voorbeeld hierboven.

MongoDB

MongoDB heeft twee soorten bewerkingen om documenten te verwijderen − verwijderEen() /verwijderVeel() и verwijderen(). Beide typen verwijderen documenten, maar retourneren verschillende resultaten.

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

Een tabel verwijderen

PostgreSQL

DROP TABLE Music;

Cassandra

Vergelijkbaar met het PostgreSQL-voorbeeld hierboven.

MongoDB

db.music.drop();

Conclusie

Het debat over de keuze tussen SQL en NoSQL woedt al meer dan 10 jaar. Er zijn twee belangrijke aspecten aan dit debat: de architectuur van de database-engine (monolithische, transactionele SQL versus gedistribueerde, niet-transactionele NoSQL) en de databaseontwerpbenadering (het modelleren van uw gegevens in SQL versus het modelleren van uw zoekopdrachten in NoSQL).

Met een gedistribueerde transactionele database zoals YugaByte DB kan het debat over database-architectuur gemakkelijk worden beëindigd. Naarmate de datavolumes groter worden dan wat er naar een enkel knooppunt kan worden geschreven, wordt een volledig gedistribueerde architectuur die lineaire schrijfschaalbaarheid ondersteunt met automatische sharding/herbalancering noodzakelijk.

Trouwens, zoals vermeld in een van de artikelen Google CloudTransactionele, sterk consistente architecturen worden nu meer gebruikt om een ​​betere ontwikkelingsflexibiliteit te bieden dan niet-transactionele, uiteindelijk consistente architecturen.

Terugkomend op de discussie over databaseontwerp kunnen we eerlijk zeggen dat beide ontwerpbenaderingen (SQL en NoSQL) noodzakelijk zijn voor elke complexe toepassing in de echte wereld. Met de SQL-aanpak voor 'datamodellering' kunnen ontwikkelaars gemakkelijker aan veranderende bedrijfsvereisten voldoen, terwijl de 'querymodellering'-benadering van NoSQL dezelfde ontwikkelaars in staat stelt om met grote hoeveelheden gegevens te werken met een lage latentie en een hoge doorvoer. Het is om deze reden dat YugaByte DB SQL- en NoSQL-API's in een gemeenschappelijke kern aanbiedt, in plaats van een van de benaderingen te promoten. Door compatibiliteit te bieden met populaire databasetalen, waaronder PostgreSQL en Cassandra, zorgt YugaByte DB er bovendien voor dat ontwikkelaars geen andere taal hoeven te leren om met een gedistribueerde, zeer consistente database-engine te werken.

In dit artikel hebben we gekeken naar hoe de basisprincipes van databaseontwerp verschillen tussen PostgreSQL, Cassandra en MongoDB. In toekomstige artikelen zullen we dieper ingaan op geavanceerde ontwerpconcepten zoals indexen, transacties, JOIN's, TTL-richtlijnen en JSON-documenten.

Wij wensen u een fijne rest van het weekend en nodigen u graag uit gratis webinar, die zal plaatsvinden op 14 mei.

Bron: www.habr.com

Voeg een reactie