Cześć przyjaciele. Przed wyjazdem na drugą część majowych wakacji dzielimy się z Wami materiałem, który przetłumaczyliśmy w oczekiwaniu na uruchomienie nowego streamu na kursie
Twórcy aplikacji spędzają dużo czasu na porównywaniu wielu operacyjnych baz danych, aby wybrać tę, która najlepiej odpowiada zamierzonemu obciążeniu. Potrzeby mogą obejmować uproszczone modelowanie danych, gwarancje transakcyjne, wydajność odczytu/zapisu, skalowanie poziome i odporność na błędy. Tradycyjnie wybór rozpoczyna się od kategorii bazy danych, SQL lub NoSQL, ponieważ każda kategoria przedstawia jasny zestaw kompromisów. Wysoka wydajność pod względem małych opóźnień i dużej przepustowości jest ogólnie postrzegana jako wymóg bezkompromisowy i dlatego jest niezbędna w przypadku każdej przykładowej bazy danych.
Celem tego artykułu jest pomoc twórcom aplikacji w dokonaniu właściwego wyboru pomiędzy SQL i NoSQL w kontekście modelowania danych aplikacji. Przyjrzymy się jednej bazie danych SQL, mianowicie PostgreSQL, oraz dwóm bazom danych NoSQL, Cassandra i MongoDB, aby omówić podstawy projektowania baz danych, takie jak tworzenie tabel, ich wypełnianie, odczytywanie danych z tabeli i usuwanie ich. W następnym artykule z pewnością przyjrzymy się indeksom, transakcjom, JOINom, dyrektywom TTL i projektowaniu baz danych w oparciu o JSON.
Jaka jest różnica między SQL i NoSQL?
Bazy danych SQL zwiększają elastyczność aplikacji dzięki gwarancji transakcyjnej ACID, a także możliwości wykonywania zapytań o dane przy użyciu funkcji JOIN w nieoczekiwany sposób w oparciu o istniejące znormalizowane relacyjne modele baz danych.
Biorąc pod uwagę ich monolityczną/jednowęzłową architekturę i wykorzystanie modelu replikacji master-slave w celu zapewnienia redundancji, tradycyjnym bazom danych SQL brakuje dwóch ważnych funkcji - skalowalności zapisu liniowego (tj. automatycznego podziału na wiele węzłów) oraz automatycznej/zerowej utraty danych. Oznacza to, że ilość odebranych danych nie może przekroczyć maksymalnej przepustowości zapisu pojedynczego węzła. Ponadto należy uwzględnić tymczasową utratę danych w ramach odporności na awarie (w architekturze typu „shared-nothing”). Tutaj należy pamiętać, że ostatnie zatwierdzenia nie zostały jeszcze odzwierciedlone w kopii slave. Aktualizacje bez przestojów są również trudne do osiągnięcia w bazach danych SQL.
Bazy danych NoSQL są zwykle dystrybuowane z natury, tj. w nich dane są podzielone na sekcje i rozproszone w kilku węzłach. Wymagają denormalizacji. Oznacza to, że wprowadzone dane również muszą zostać skopiowane kilkukrotnie, aby móc odpowiedzieć na konkretne, przesłane przez Ciebie zapytania. Ogólnym celem jest uzyskanie wysokiej wydajności poprzez zmniejszenie liczby fragmentów dostępnych podczas odczytu. Oznacza to, że NoSQL wymaga modelowania zapytań, podczas gdy SQL wymaga modelowania danych.
NoSQL koncentruje się na osiągnięciu wysokiej wydajności w rozproszonym klastrze i jest to podstawowe uzasadnienie wielu kompromisów w projektowaniu baz danych, które obejmują utratę transakcji ACID, JOIN i spójne globalne indeksy wtórne.
Istnieje argument, że chociaż bazy danych NoSQL zapewniają liniową skalowalność zapisu i wysoką odporność na błędy, utrata gwarancji transakcyjnych sprawia, że nie nadają się one do przechowywania danych o znaczeniu krytycznym.
Poniższa tabela pokazuje, czym modelowanie danych w NoSQL różni się od SQL.
SQL i NoSQL: dlaczego potrzebne są oba?
Aplikacje w świecie rzeczywistym z dużą liczbą użytkowników, takie jak Amazon.com, Netflix, Uber i Airbnb, mają za zadanie wykonywanie złożonych, wieloaspektowych zadań. Na przykład aplikacja e-commerce, taka jak Amazon.com, musi przechowywać lekkie, bardzo krytyczne dane, takie jak informacje o użytkowniku, produkty, zamówienia, faktury, wraz z ciężkimi, mniej wrażliwymi danymi, takimi jak recenzje produktów, wiadomości wsparcia, aktywność użytkownika, opinie i rekomendacje użytkowników. Naturalnie aplikacje te opierają się na co najmniej jednej bazie danych SQL i co najmniej jednej bazie danych NoSQL. W systemach międzyregionalnych i globalnych baza danych NoSQL działa jako rozproszona geograficznie pamięć podręczna dla danych przechowywanych w zaufanej źródłowej bazie danych SQL działającej w określonym regionie.
W jaki sposób YugaByte DB łączy SQL i NoSQL?
Zbudowany w oparciu o silnik pamięci masowej zorientowany na dzienniki, automatyczne dzielenie na fragmenty, rozproszoną replikację konsensusową na fragmenty i rozproszone transakcje ACID (inspirowane Google Spanner), YugaByte DB to pierwsza na świecie baza danych typu open source, która jest jednocześnie kompatybilna z NoSQL (Cassandra i Redis) i SQL (PostgreSQL). Jak pokazano w poniższej tabeli, YCQL, interfejs API YugaByte DB kompatybilny z Cassandrą, dodaje koncepcje jedno- i wielokluczowych transakcji ACID oraz globalnych indeksów wtórnych do interfejsu API NoSQL, rozpoczynając w ten sposób erę transakcyjnych baz danych NoSQL. Dodatkowo YCQL, interfejs API YugaByte DB kompatybilny z PostgreSQL, dodaje koncepcje liniowego skalowania zapisu i automatycznej odporności na błędy do interfejsu API SQL, udostępniając światu rozproszone bazy danych SQL. Ponieważ YugaByte DB ma charakter transakcyjny, API NoSQL można teraz używać w kontekście danych o znaczeniu krytycznym.
Jak już wspomniano w artykule
- Jeśli Twoim głównym obciążeniem są wieloklawiszowe operacje JOIN, wybierając YSQL, pamiętaj, że Twoje klucze mogą być rozproszone w wielu węzłach, co skutkuje większymi opóźnieniami i/lub niższą przepustowością niż NoSQL.
- W przeciwnym razie wybierz jeden z dwóch interfejsów API NoSQL, pamiętając, że lepszą wydajność uzyskasz w wyniku zapytań obsługiwanych z jednego węzła na raz. YugaByte DB może służyć jako pojedyncza operacyjna baza danych dla złożonych aplikacji w świecie rzeczywistym, które wymagają jednoczesnego zarządzania wieloma obciążeniami.
Laboratorium modelowania danych w następnej sekcji opiera się na bazach danych YugaByte DB zgodnych z PostgreSQL i Cassandra API, w przeciwieństwie do natywnych baz danych. Podejście to kładzie nacisk na łatwość interakcji z dwoma różnymi interfejsami API (na dwóch różnych portach) tego samego klastra bazy danych, w przeciwieństwie do stosowania całkowicie niezależnych klastrów dwóch różnych baz danych.
W kolejnych sekcjach przyjrzymy się laboratorium modelowania danych, aby zilustrować różnice i niektóre cechy wspólne omawianych baz danych.
Laboratorium Modelowania Danych
Instalacja bazy danych
Biorąc pod uwagę nacisk na projektowanie modeli danych (a nie na złożone architektury wdrażania), zainstalujemy bazy danych w kontenerach Docker na komputerze lokalnym, a następnie będziemy z nimi wchodzić w interakcję za pomocą odpowiednich powłok wiersza poleceń.
Baza danych YugaByte DB kompatybilna z PostgreSQL i Cassandrą
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
Dostęp do wiersza poleceń
Połączmy się z bazami danych za pomocą powłoki wiersza poleceń dla odpowiednich interfejsów API.
PostgreSQL
docker exec -it yb-postgres-n1 /home/yugabyte/postgres/bin/psql -p 5433 -U postgres
Cassandra
cqlsh
w katalogu bin
.
Należy zauważyć, że CQL został zainspirowany językiem SQL i ma podobne koncepcje tabel, wierszy, kolumn i indeksów. Jednak jako język NoSQL dodaje pewien zestaw ograniczeń, z których większość omówimy również w innych artykułach.
docker exec -it yb-tserver-n1 /home/yugabyte/bin/cqlsh
MongoDB
docker exec -it my-mongo bash
cd bin
mongo
Utwórz tabelę
Teraz możemy wchodzić w interakcję z bazą danych, aby wykonywać różne operacje za pomocą wiersza poleceń. Zacznijmy od stworzenia tabeli przechowującej informacje o piosenkach napisanych przez różnych artystów. Te utwory mogą stanowić część albumu. Opcjonalne atrybuty utworu to także rok wydania, cena, gatunek i ocena. Musimy uwzględnić dodatkowe atrybuty, które mogą być potrzebne w przyszłości poprzez pole „tagi”. Może przechowywać dane częściowo ustrukturyzowane w postaci par klucz-wartość.
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
Tworzenie tabeli w Cassandrze jest bardzo podobne do PostgreSQL. Jedną z głównych różnic jest brak ograniczeń integralności (np. NOT NULL), ale za to odpowiada aplikacja, a nie baza danych NoSQL. Klucz podstawowy składa się z klucza partycji (kolumna Artysta w poniższym przykładzie) i zestawu kolumn grupujących (kolumna SongTitle w poniższym przykładzie). Klucz partycji określa, w której partycji/fragmencie należy umieścić wiersz, a kolumny grupowania wskazują, w jaki sposób dane powinny być zorganizowane w bieżącym fragmencie.
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 organizuje dane w bazy danych (baza danych) (podobnie jak Keyspace w Cassandrze), w których znajdują się kolekcje (podobnie jak tabele) zawierające dokumenty (podobnie jak wiersze w tabeli). W MongoDB w zasadzie nie ma potrzeby definiowania początkowego schematu. Zespół „użyj bazy danych”pokazany poniżej tworzy instancję bazy danych przy pierwszym wywołaniu i zmienia kontekst nowo utworzonej bazy danych. Nawet kolekcje nie muszą być tworzone bezpośrednio; są tworzone automatycznie, po prostu podczas dodawania pierwszego dokumentu do nowej kolekcji. Należy pamiętać, że MongoDB domyślnie korzysta z testowej bazy danych, więc każda operacja na poziomie kolekcji bez określenia konkretnej bazy danych będzie domyślnie na niej uruchamiana.
use myNewDatabase;
Uzyskiwanie informacji o stole
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;
Wprowadzanie danych do tabeli
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
Ogólny wyraz INSERT
w Cassandrze wygląda bardzo podobnie do tego w PostgreSQL. Istnieje jednak jedna duża różnica w semantyce. W Cassandrze INSERT
jest właściwie operacją UPSERT
, gdzie do wiersza dodawane są ostatnie wartości, jeśli wiersz już istnieje.
Wprowadzanie danych jest podobne do PostgreSQL
INSERT
powyżej
.
MongoDB
Mimo że MongoDB jest bazą danych NoSQL, taką jak Cassandra, operacja wstawiania nie ma nic wspólnego z semantycznym zachowaniem Cassandry. W MongoDB UPSERT
, co czyni go podobnym do PostgreSQL. Dodawanie domyślnych danych bez _idspecified
spowoduje dodanie nowego dokumentu do kolekcji.
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"
}
}
);
Zapytanie o tabelę
Być może najbardziej znaczącą różnicą między SQL i NoSQL pod względem konstrukcji zapytań jest używany język FROM
и WHERE
. SQL pozwala na wyrażenie po FROM
wybierz wiele tabel i wyrażenie za pomocą WHERE
może mieć dowolną złożoność (w tym operacje JOIN
między stołami). Jednak NoSQL ma tendencję do nakładania poważnych ograniczeń FROM
i pracuj tylko z jedną określoną tabelą oraz in WHERE
, należy zawsze określić klucz podstawowy. Wiąże się to ze zwiększeniem wydajności NoSQL, o którym mówiliśmy wcześniej. To pragnienie prowadzi do wszelkiej możliwej redukcji wszelkich interakcji między tabelami i kluczami. Może wprowadzić duże opóźnienie w komunikacji między węzłami podczas odpowiadania na żądanie i dlatego ogólnie najlepiej go unikać. Na przykład Cassandra wymaga, aby zapytania były ograniczone do niektórych operatorów (tylko =, IN, <, >, =>, <=
) na kluczach partycji, z wyjątkiem żądania indeksu dodatkowego (dozwolony jest tutaj tylko operator =).
PostgreSQL
Poniżej znajdują się trzy przykłady zapytań, które można łatwo wykonać w bazie danych SQL.
- Wyświetl wszystkie utwory danego artysty;
- Wyświetl wszystkie utwory artysty, które pasują do pierwszej części tytułu;
- Wyświetl wszystkie utwory danego artysty, które mają w tytule określone słowo i mają cenę niższą niż 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
Z wymienionych powyżej zapytań PostgreSQL tylko pierwsze będzie działać w Cassandrze bez zmian, ponieważ operator LIKE
nie można zastosować do grupowania kolumn, takich jak SongTitle
. W takim przypadku dozwolone są tylko operatorzy =
и 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
Jak pokazano w poprzednich przykładach, główną metodą tworzenia zapytań w MongoDB jest music
w przykładzie poniżej), dlatego wykonywanie zapytań do wielu kolekcji jest zabronione.
db.music.find( {
artist: "No One You Know"
}
);
db.music.find( {
artist: "No One You Know",
songTitle: /Call/
}
);
Czytanie wszystkich wierszy tabeli
Odczyt wszystkich wierszy jest po prostu szczególnym przypadkiem wzorca zapytania, który omawialiśmy wcześniej.
PostgreSQL
SELECT *
FROM Music;
Cassandra
Podobny do powyższego przykładu PostgreSQL.
MongoDB
db.music.find( {} );
Edycja danych w tabeli
PostgreSQL
PostgreSQL dostarcza instrukcje UPDATE
zmienić dane. Ona nie ma żadnych możliwości UPSERT
, więc ta instrukcja nie powiedzie się, jeśli wiersza nie ma już w bazie danych.
UPDATE Music
SET Genre = 'Disco'
WHERE Artist = 'The Acme Band' AND SongTitle = 'Still In Love';
Cassandra
Cassandra ma UPDATE
podobny do PostgreSQL. UPDATE
ma tę samą semantykę UPSERT
, tak jak INSERT
.
Podobny do powyższego przykładu PostgreSQL.
MongoDB
operacja UPSERT
. Aktualizowanie wielu dokumentów i podobne zachowanie UPSERT
można zastosować, ustawiając dodatkowe flagi dla operacji. Na przykład w poniższym przykładzie gatunek konkretnego artysty jest aktualizowany na podstawie jego utworu.
db.music.update(
{"artist": "The Acme Band"},
{
$set: {
"genre": "Disco"
}
},
{"multi": true, "upsert": true}
);
Usuwanie danych z tabeli
PostgreSQL
DELETE FROM Music
WHERE Artist = 'The Acme Band' AND SongTitle = 'Look Out, World';
Cassandra
Podobny do powyższego przykładu PostgreSQL.
MongoDB
MongoDB ma dwa rodzaje operacji usuwania dokumentów −
db.music.deleteMany( {
artist: "The Acme Band"
}
);
Usuwanie tabeli
PostgreSQL
DROP TABLE Music;
Cassandra
Podobny do powyższego przykładu PostgreSQL.
MongoDB
db.music.drop();
wniosek
Debata na temat wyboru pomiędzy SQL a NoSQL trwa już od ponad 10 lat. Debata ta obejmuje dwa główne aspekty: architekturę silnika bazy danych (monolityczny, transakcyjny SQL vs rozproszony, nietransakcyjny NoSQL) oraz podejście do projektowania baz danych (modelowanie danych w SQL vs modelowanie zapytań w NoSQL).
Dzięki rozproszonej transakcyjnej bazie danych, takiej jak YugaByte DB, można łatwo uciszyć debatę na temat architektury baz danych. Ponieważ wolumeny danych stają się większe niż to, co można zapisać w pojedynczym węźle, konieczna staje się w pełni rozproszona architektura obsługująca skalowalność zapisu liniowego z automatycznym fragmentowaniem/ponownym równoważeniem.
Poza tym, jak stwierdzono w jednym z artykułów
Wracając do dyskusji na temat projektowania baz danych, można śmiało powiedzieć, że oba podejścia do projektowania (SQL i NoSQL) są niezbędne w przypadku każdej złożonej aplikacji w świecie rzeczywistym. Podejście oparte na „modelowaniu danych” SQL umożliwia programistom łatwiejsze spełnianie zmieniających się wymagań biznesowych, podczas gdy podejście „modelowania zapytań” NoSQL umożliwia tym samym programistom działanie na dużych ilościach danych przy niskim opóźnieniu i dużej przepustowości. Z tego powodu YugaByte DB udostępnia interfejsy API SQL i NoSQL we wspólnym rdzeniu, zamiast promować jedno z podejść. Dodatkowo, zapewniając kompatybilność z popularnymi językami baz danych, w tym PostgreSQL i Cassandra, YugaByte DB gwarantuje, że programiści nie muszą uczyć się innego języka, aby pracować z rozproszonym, wysoce spójnym silnikiem bazy danych.
W tym artykule przyjrzeliśmy się, jak różnią się podstawy projektowania baz danych między PostgreSQL, Cassandra i MongoDB. W przyszłych artykułach zagłębimy się w zaawansowane koncepcje projektowe, takie jak indeksy, transakcje, JOIN, dyrektywy TTL i dokumenty JSON.
Życzymy udanego wypoczynku w weekend i zapraszamy
Źródło: www.habr.com