Basisprincipes van gegevensmodellering - PostgreSQL versus Cassandra versus MongoDB

Applicatieontwikkelaars besteden meestal veel tijd aan het evalueren van meerdere operationele databases om die ene database te vinden die het beste past bij hun behoeften aan workloads. Deze behoeften omvatten vereenvoudigde gegevensmodellering, transactionele garanties, lees- / schrijfprestaties, horizontale schaling en fouttolerantie. Traditioneel begint deze selectie met de SQL versus NoSQL database categorieën omdat elke categorie een duidelijke set van afwegingen biedt. Hoge prestaties in termen van lage latentie en hoge doorvoer worden meestal als een niet-compromisbare vereiste beschouwd en worden daarom in elke gekozen database verwacht.

Dit bericht is bedoeld om applicatieontwikkelaars te helpen de keuze van SQL versus NoSQL te begrijpen in de context van de datamodelleringbehoeften van een applicatie. We gebruiken één SQL-database, namelijk PostgreSQL, en twee NoSQL-databases, namelijk Cassandra en MongoDB, als voorbeelden om de basismodellen voor gegevensmodellering uit te leggen, zoals het maken van tabellen, het invoegen van gegevens, het uitvoeren van scans en het verwijderen van gegevens. In een vervolgpost behandelen we geavanceerde onderwerpen zoals indexen, transacties, joins, time-to-live (TTL) -richtlijnen en op JSON gebaseerde documentgegevensmodellering.

Hoe verschilt NoSQL van SQL in gegevensmodellering?

SQL-databases verhogen de wendbaarheid van applicaties door middel van ACID-transactionele garanties en door de mogelijkheid om gegevens op te vragen met JOINs op onvoorziene manieren bovenop bestaande genormaliseerde relationele datamodellen.

Gezien hun monolithische / single-node-architectuur en het gebruik van een master-slave-replicatiemodel voor redundantie, missen traditionele SQL-databases twee belangrijke mogelijkheden: lineaire schrijfschaalbaarheid (d.w.z. automatische sharding over meerdere knooppunten) en failover van automatisch / nul gegevensverlies. Dit betekent dat opgenomen datavolumes de maximale schrijfdoorvoer van een enkel knooppunt niet kunnen overschrijden. Bovendien moet er wat tijdelijk gegevensverlies worden verwacht bij failover (bij shared niets-opslagarchitecturen), gezien het feit dat de recente commits nog niet zouden zijn verschenen bij de slave-replica. Nul downtime upgrades zijn ook erg moeilijk te realiseren in de SQL database wereld.

NoSQL DB's worden meestal in de natuur gedistribueerd waarbij gegevens worden verdeeld of verdeeld over meerdere knooppunten. Ze verplichten denormalisatie, wat betekent dat ingevoegde gegevens ook meerdere keren moeten worden gekopieerd om de specifieke vragen te beantwoorden die u voor ogen hebt. Het overkoepelende doel is om hoge prestaties te halen door het aantal toegang tot scherven tijdens de leestijd expliciet te verminderen. Vandaar de verklaring dat NoSQL vereist dat u uw vragen modelleert, terwijl SQL vereist dat u uw gegevens modelleert.

NoSQL's focus op het behalen van hoge prestaties op een gedistribueerd cluster wordt vermeld als de primaire reden voor compromissen met meerdere gegevensmodellering, waaronder verlies van ACID-transacties, JOIN's en consistente wereldwijde secundaire indexen.

De algemene perceptie is dat hoewel NoSQL-databases lineaire schrijfschaalbaarheid en hoge fouttolerantie bieden, het verlies van transactionele garanties ze ongeschikt maakt voor missiekritieke gegevens.

De volgende tabel beschrijft hoe NoSQL-gegevensmodellering verschilt van die van SQL.

SQL versus NoSQL - verschillen in belangrijkste gegevensmodellering

SQL & NoSQL: Waarom hebt u beide nodig?

De meeste real-world applicaties met boeiende gebruikerservaringen zoals Amazon.com, Netflix, Uber en Airbnb worden intern aangedreven door een complexe mix van meerdere workloads. Bijv. een e-commerce-applicatie zoals Amazon.com moet laagvolume, zeer missiekritieke gegevens zoals gebruikers, producten, bestellingen, facturen opslaan naast hoogvolume, minder missiekritieke gegevens zoals productrecensies, helpdeskberichten, gebruikersactiviteit, gebruikersaanbevelingen. Uiteraard vertrouwen deze toepassingen op ten minste één SQL-database naast ten minste één NoSQL-database. Bij multiregionale en wereldwijde implementaties fungeert de NoSQL-database ook als geodistributiecache voor de gegevens die zijn opgeslagen in de bron van de waarheid, de SQL-database die in een enkele regio wordt uitgevoerd.

Hoe YugaByte DB SQL & NoSQL samenbrengt op een gemeenschappelijke databasekern?

YugaByte DB is gebouwd op een unieke combinatie van log-gestructureerde samenvoegopslag-engine, auto-sharding, per shard gedistribueerde consensusreplicatie en gedistribueerde ACID-transacties (geïnspireerd door Google Spanner). Dit is 's werelds eerste open source database die beide NoSQL is (Cassandra & Redis compatibel) en SQL (compatibel met PostgreSQL) tegelijkertijd. Zoals weergegeven in de onderstaande tabel, voegt YCQL, de Cassandra-compatibele API van YugaByte DB, het idee van single-key en multi-key ACID-transacties en wereldwijde secundaire indexen toe aan NoSQL API's, waarmee het tijdperk van Transactional NoSQL wordt ingeluid. Bovendien voegt YSQL, de PostgreSQL-compatibele API van YugaByte DB, de noties van lineaire schrijfschaling en automatische fouttolerantie toe aan een SQL API, waardoor de wereld van gedistribueerde SQL naar voren komt. Omdat YugaByte DB transactie in de kern is, kunnen zelfs de NoSQL API's nu worden gebruikt in de context van missiekritieke gegevens.

YugaByte DB - SQL & NoSQL op een enkele databasekern

Zoals eerder beschreven in Introductie van YSQL: een PostgreSQL-compatibele gedistribueerde SQL API voor YugaByte DB, hangt de keuze van SQL versus NoSQL in YugaByte DB volledig af van de kenmerken van de meeste workloads.

  • Als de meeste workloads multi-key-bewerkingen zijn met JOINS, kies dan YSQL met dien verstande dat uw sleutels kunnen worden verdeeld over meerdere knooppunten, wat leidt tot een hogere latentie en / of een lagere doorvoer dan NoSQL.
  • Kies anders een van de twee NoSQL API's, met dien verstande dat u betere prestatievoordelen krijgt als gevolg van vragen die in de eerste plaats per knooppunt worden beantwoord. YugaByte DB kan dienen als de uniforme operationele database voor complexe real-world apps die meestal meerdere workloads tegelijkertijd moeten beheren.

Het datamodelleringslaboratorium in de volgende sectie is gebaseerd op YugaByte DB's PostgreSQL en Cassandra compatibele API's in tegenstelling tot de originele databases. Deze benadering benadrukt de eenvoud 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 zullen we door een datamodellering in het lab lopen om veel van de verschillen en een paar overeenkomsten tussen verschillende databases te illustreren.

Data Modelling Lab

Databases installeren

Gezien de focus op datamodellering (en niet op complexe implementatie-architecturen), zullen we de databases in Docker-containers op onze lokale machines installeren en vervolgens met hen communiceren via hun respectieve opdrachtregel shells.

YugaByte DB, een PostgreSQL & Cassandra compatibele database

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

MongoDB

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

Toegang via Command Line Shell

Laten we vervolgens verbinding maken met de databases met behulp van de opdrachtregel shells voor de respectieve API's.

PostgreSQL

psql is een opdrachtregel-shell voor interactie met PostgreSQL. Voor het gebruiksgemak wordt YugaByte DB geleverd met een versie van psql in de bin-directory.

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

Cassandra

cqlsh is een opdrachtregel-shell voor interactie met Cassandra en zijn compatibele databases via CQL (de Cassandra Query Language). Voor het gebruiksgemak wordt YugaByte DB geleverd met een versie van cqlsh in de bin-directory.

Merk op dat CQL sterk is geïnspireerd door SQL met een vergelijkbaar idee van tabellen, rijen, kolommen en indexen. Als NoSQL-taal voegt het echter een specifieke set beperkingen toe, waarvan we de meeste zullen bespreken tijdens onze blogserie.

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

MongoDB

mongo is een command line shell voor interactie met MongoDB. Het kan worden gevonden in de map bin van een MongoDB-installatie.

docker voer -it my-mongo bash
cd-bak
mongo

Maak een tabel

We kunnen nu communiceren met de database voor verschillende bewerkingen met behulp van de opdrachtregel shell. Laten we beginnen met het maken van een tabel met informatie over door artiesten gepubliceerde nummers. Deze nummers maken soms deel uit van een album. De andere optionele kenmerken van een nummer zijn jaar uitgebracht, prijs, genre en criticusbeoordeling. We hebben account nodig voor extra attributen die we in de toekomst mogelijk nodig hebben via een ‘tags’ -veld dat semi-gestructureerde gegevens kan opslaan als sleutel / waarde-paren.

PostgreSQL

CREATE TABLE Muziek (
    Artiest VARCHAR (20) NOT NULL,
    SongTitle VARCHAR (30) NIET NULL,
    AlbumTitel VARCHAR (25),
    Jaar INT,
    Prijs FLOAT,
    Genre VARCHAR (10),
    Criticating FLOAT,
    Tags TEKST,
    PRIMAIRE TOETS (Artiest, SongTitle)
);

Cassandra

De tabel Maken in Cassandra lijkt erg op die van PostgreSQL. Een groot verschil is het gebrek aan integriteitsbeperkingen (zoals NIET NULL), wat de verantwoordelijkheid is van de toepassing en niet van de database in de NoSQL-wereld. De primaire sleutel bestaat uit de partitiesleutel (de kolom Artiest in het onderstaande voorbeeld) en een set clusterkolommen (de kolom SongTitle in het onderstaande voorbeeld). De partitiesleutel bepaalt in welke partitie / scherf de rij moet worden geplaatst en de clusterkolommen geven aan hoe de gegevens binnen een bepaalde scherf moeten worden georganiseerd.

KEYSPACE MAKEN myapp;
GEBRUIK myapp;
CREATE TABLE Muziek (
    Kunstenaar TEKST,
    SongTitle TEXT,
    AlbumTitel TEXT,
    Jaar INT,
    Prijs FLOAT,
    Genre TEKST,
    Criticating FLOAT,
    Tags TEKST,
    PRIMAIRE TOETS (Artiest, SongTitle)
);

MongoDB

MongoDB organiseert gegevens in databases (gelijk aan Cassandra Keyspace) met collecties (gelijk aan tabellen) met documenten (gelijk aan een rij in een tabel). Als een 'schemaless'-database is het vooraf definiëren van het schema niet noodzakelijk in MongoDB. Het onderstaande commando 'database gebruiken' maakt een database direct de eerste keer dat deze wordt opgeroepen, samen met de contextverandering in de nieuw gecreëerde database. Zelfs collecties hoeven niet expliciet te worden gemaakt, maar worden eerder automatisch gemaakt door het eerste document eenvoudig in een nieuwe collectie in te voegen. Merk op dat de standaarddatabase van MongoDB een test is, dus elke bewerking op collectieniveau die wordt uitgevoerd zonder de database op te geven, wordt uitgevoerd in deze standaardcontext.

gebruik myNewDatabase;

Informatie krijgen over een tabel

PostgreSQL

\ d muziek
Tabel "public.music"
    Kolom | Type | Sorteren | Nullable | Standaard
-------------- + ----------------------- + ----------- + ---------- + --------
 kunstenaar | karakter variërend (20) | | niet nul |
 songtitle | karakter variërend (30) | | niet nul |
 albumtitel | karakter variërend (25) | | |
 jaar | geheel getal | | |
 prijs | dubbele precisie | | |
 genre | karakter variërend (10) | | |
 bekritiseren | dubbele precisie | | |
 tags | tekst | | |
indexen:
    "music_pkey" PRIMARY KEY, btree (artiest, songtitel)

Cassandra

BESCHRIJF TAFELMUZIEK;
CREATE TABLE myapp.music (
    kunstenaar tekst,
    songtekst tekst,
    albumtitel tekst,
    jaar int,
    prijs vlotter,
    genre tekst,
    tags tekst,
    PRIMAIRE KEY (artiest, songtitel)
) MET CLUSTERING ORDER BY (songtitle ASC)
    EN default_time_to_live = 0
    AND transacties = {'enabled': 'false'};

MongoDB

gebruik myNewDatabase;
collecties tonen;

Gegevens invoegen in een tabel

PostgreSQL

PLAATSEN IN Muziek
    (Artiest, SongTitle, AlbumTitle,
    Jaar, prijs, genre, kritiek,
    Tags)
WAARDEN (
    'Niemand die je kent', 'Bel me vandaag', 'enigszins beroemd',
    2015, 2.14, 'Land', 7.8,
    '{"Componisten": ["Smith", "Jones", "Davis"], "LengthInSeconds": 214}'
);
PLAATSEN IN Muziek
    (Artiest, SongTitle, AlbumTitle,
    Prijs, Genre, CriticRating)
WAARDEN (
    'Niemand die je kent', 'My Dog Spot', 'Hey Now',
    1,98, 'Land', 8.4
);
PLAATSEN IN Muziek
    (Artiest, SongTitle, AlbumTitle,
    Prijs, genre)
WAARDEN (
    'The Acme Band', 'Look Out, World', 'The Buck Starts Here',
    0.99, 'Rock'
);
PLAATSEN IN Muziek
    (Artiest, SongTitle, AlbumTitle,
    Prijs, genre,
    Tags)
WAARDEN (
    'The Acme Band', 'Still In Love', 'The Buck Starts Here',
    2.47, 'Rock',
    '{"radioStationsPlaying": ["KHCR", "KBQX", "WTNR", "WJJH"], "tourDates": {"Seattle": "20150625", "Cleveland": "20150630"}, "rotatie": heavy}'
);

Cassandra

De INSERT-instructies van Cassandra lijken in het algemeen erg op die van PostgreSQL. Er is echter een groot verschil in semantiek. INSERT is eigenlijk een upsert-bewerking in Cassandra waarbij de rij wordt bijgewerkt met de nieuwste waarden voor het geval dat de rij al bestaat.

Hetzelfde als de bovenstaande PostgreSQL INSERT-instructies.

MongoDB

Hoewel MongoDB ook een NoSQL-database is die vergelijkbaar is met Cassandra, heeft de invoegbewerking niet hetzelfde semantische gedrag als Cassandra. MongoDB insert () heeft geen upsert-mogelijkheid waardoor het vergelijkbaar is met PostgreSQL. Het standaard invoeggedrag zonder _idspecified leidt tot een nieuw document dat aan de verzameling wordt toegevoegd.

db.music.insert ({
artiest: "Niemand die je kent",
   songTitle: "Call Me Today",
    albumTitel: "Somewhat Famous",
    jaar: 2015,
    prijs: 2.14,
    genre: "Country",
    tags: {
Componisten: ["Smith", "Jones", "Davis"],
Lengte: seconden
}
   }
);
db.music.insert ({
    artiest: "Niemand die je kent",
    songTitle: "My Dog Spot",
    albumTitel: "Hey Now",
    prijs: 1.98,
    genre: "Country",
    kritiek: 8.4
   }
);
db.music.insert ({
    artiest: "The Acme Band",
    songTitle: "Look Out, World",
    albumTitle: "The Buck Starts Here",
    prijs: 0.99,
    genre: "Rock"
   }
);
db.music.insert ({
    artiest: "The Acme Band",
    songTitle: "Still In Love",
    albumTitle: "The Buck Starts Here",
    prijs: 2.47,
    genre: "Rock",
    tags: {
        radioStationsPlaying: ["KHCR", "KBQX", "WTNR", "WJJH"],
        reisdata: {
            Seattle: "20150625",
            Cleveland: "20150630"
        },
        rotatie: "Heavy"
}
    }
);

Vraag een tafel

Het belangrijkste verschil tussen SQL en NoSQL op het gebied van modelleringsquery's is waarschijnlijk het gebruik van de clausules FROM en WHERE. Met SQL kan de clausule FROM meerdere tabellen bevatten en de WHERE-clausule een willekeurige complexiteit hebben (inclusief JOINs voor meerdere tabellen). NoSQL heeft echter de neiging om een ​​harde beperking op te leggen aan de clausule FROM om slechts één tabel te hebben en aan de WHERE-clausule om altijd de primaire sleutel op te geven. Dit komt door de hoge prestatiegerichte focus van NoSQL die we eerder hebben besproken en die is gericht op het verminderen van elke inter-tabel en cross-key interactie. Een dergelijke interactie kan kruis-knooppuntcommunicatie met hoge latentie introduceren in de reactietijd van de vraag en kan daarom het beste helemaal worden vermeden. Bijv. Cassandra vereist dat query's worden beperkt door operatoren (alleen =, IN, <,>, =>, <= zijn toegestaan) op partitiesleutels behalve bij het doorzoeken van een secundaire index (waar alleen = operator is toegestaan).

PostgreSQL

Hier volgen 3 soorten vragen die gemakkelijk kunnen worden bediend door een SQL-database.

  • Retourneer alle nummers van een artiest
  • Retourneer alle nummers van een artiest, overeenkomend met het eerste deel van de titel
  • Retourneer alle nummers van een artiest, met een bepaald woord in de titel, maar alleen als de prijs lager is dan 1,00
SELECTEER * VAN Muziek
WHERE Artist = 'Niemand die je kent';
SELECTEER * VAN Muziek
WAAR Artiest = 'Niemand die je kent' EN SongTitle ZOALS 'Call%';
SELECTEER * VAN Muziek
WAAR Artiest = 'Niemand die je kent' EN SongTitle ALS '% Today%'
EN Prijs> 1,00;

Cassandra

Van de PostgreSQL-zoekopdrachten die hierboven worden vermeld, werkt alleen de eerste ongewijzigd met Cassandra, aangezien de LIKE-operator niet is toegestaan ​​op clusterkolommen zoals SongTitle. Alleen operatoren = en IN zijn in dit geval toegestaan.

SELECTEER * VAN Muziek
WHERE Artist = 'Niemand die je kent';
SELECTEER * VAN Muziek
WAAR Artiest = 'Niemand die je kent' EN SongTitle IN ('Call Me Today', 'My Dog Spot')
EN Prijs> 1,00;

MongoDB

Zoals getoond in de vorige voorbeelden, is de primaire methode voor het opvragen van MongoDB de methode db.collection.find (). Deze methode wordt gekwalificeerd door de collectienaam (muziek in het onderstaande voorbeeld) en wordt zeer expliciet opgevraagd, dus het doorzoeken in collecties is expliciet niet toegestaan.

db.music.find ({
  artiest: "Niemand die je kent"
 }
);
db.music.find ({
  artiest: "Niemand die je kent",
  songTitle: / Call /
 }
);

Alle rijen van een tabel lezen

Alle rijen lezen is gewoon een speciaal geval van het generieke querypatroon dat we eerder hebben waargenomen.

PostgreSQL

SELECT *
VAN Muziek;

Cassandra

Hetzelfde als de PostgreSQL SELECT-instructie hierboven.

MongoDB

db.music.find ({});

Wijzig gegevens in een tabel

PostgreSQL

PostgreSQL biedt de UPDATE-instructie voor het wijzigen van gegevens. Het staat geen upsert-mogelijkheid toe, dus de instructie zal mislukken als de rij nog niet in de database bestaat.

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

Cassandra

Cassandra heeft ook een UPDATE-instructie vergelijkbaar met PostgreSQL. UPDATE heeft ook dezelfde upsert-semantiek als die van de INSERT-instructie.

Hetzelfde als de bovenstaande PostgreSQL UPDATE-instructie.

MongoDB

De bewerking update () van MongoDB kan een bestaand document volledig bijwerken of kan alleen specifieke velden bijwerken. Standaard wordt slechts één document bijgewerkt met upsert semantiek uitgeschakeld. Multi-document updates en upsert-gedrag kunnen worden ingeschakeld door extra vlaggen voor de bewerking in te stellen. Bijv. het onderstaande voorbeeld werkt het genre van een specifieke artiest bij voor alle nummers van de artiest.

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

Gegevens verwijderen uit een tabel

PostgreSQL

VERWIJDEREN UIT Muziek
WHERE Artist = 'The Acme Band' EN SongTitle = 'Look Out, World';

Cassandra

Hetzelfde als de PostgreSQL DELETE-instructie hierboven.

MongoDB

MongoDB heeft twee soorten bewerkingen voor het verwerken van documentverwijderingen - deleteOne () / deleteMany () en remove (). Beide verwijderen document (en) maar hebben verschillende retourresultaten.

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

Verwijder een tafel

PostgreSQL

DROP TABLE Muziek;

Cassandra

Hetzelfde als de PostgreSQL DROP TABLE-instructie hierboven;

MongoDB

db.music.drop ();

Overzicht

Het debat over SQL versus NoSQL woedt nu al meer dan tien jaar. Er zijn 2 aspecten van dit debat: de database-kernarchitectuur (monolithisch, transactie-SQL versus gedistribueerd, niet-transactie NoSQL) en de gegevensmodelleringsbenadering (model uw gegevens in SQL versus model uw vragen in NoSQL).

Met een gedistribueerde, transactionele database zoals YugaByte DB, kan de database-kernarchitectuur van het debat gemakkelijk tot rust worden gebracht. Naarmate datavolumes groter worden dan wat in een enkel knooppunt kan worden geschreven, wordt een volledig gedistribueerde architectuur die lineaire schrijfschaalbaarheid mogelijk maakt met automatische sharding / rebalancing een must-have. Bovendien, zoals beschreven in dit bericht van Google Cloud, worden transactionele, sterk consistente architecturen nu algemeen geaccepteerd om hogere ontwikkelaar en operationele flexibiliteit te bieden dan niet-transactionele, uiteindelijk consistente architecturen.

In het debat over datamodellering is het redelijk om te zeggen dat zowel de SQL- als NoSQL-datamodelleringsbenaderingen essentieel zijn voor elke complexe real-world toepassing. Met de model-uw-gegevensbenadering van SQL kunnen ontwikkelaars gemakkelijker inspelen op veranderende zakelijke vereisten, terwijl de model-uw-vragenbenadering van NoSQL dezelfde ontwikkelaars in staat stelt grote gegevensvolumes te beheren met een lage latentie en een hoge doorvoer. Dit is precies de reden dat YugaByte DB zowel SQL- als NoSQL-API's implementeert op de gemeenschappelijke kern in plaats van te promoten dat de ene aanpak strikt beter is dan de andere. Bovendien zorgt YugaByte DB ervoor dat ontwikkelaars geen andere taal hoeven te leren om te kunnen profiteren van de gedistribueerde, sterk consistente databasekern door te zorgen voor compatibiliteit met de draad met populaire databasetalen, waaronder PostgreSQL en Cassandra.

Dit bericht heeft ons geholpen te begrijpen hoe de basisprincipes van gegevensmodellering verschillen tussen PostgreSQL, Cassandra en MongoDB. In de volgende berichten in de serie gaan we dieper in op geavanceerde datamodelleringconcepten, zoals indexen, transacties, JOINs, TTL-richtlijnen en JSON-documenten.

Wat is het volgende?

  • Vergelijk YugaByte DB met databases zoals Amazon DynamoDB, Cassandra, MongoDB en Azure Cosmos DB.
  • Aan de slag met YugaByte DB op macOS, Linux, Docker en Kubernetes.
  • Neem contact met ons op voor meer informatie over licenties, prijzen of om een ​​technisch overzicht te plannen.

Oorspronkelijk gepubliceerd op de YugaByte Database Blog.