Привіт, друзі. Перед відходом на другу частину травневих свят ділимося з вами матеріалом, який ми перевели напередодні запуску нового потоку за курсом
Розробники додатків витрачають багато часу на порівняння кількох операційних баз даних, щоб вибрати ту, яка найкраще підійде для передбачуваного робочого навантаження. Потреби можуть включати спрощене моделювання даних, транзакційні гарантії, продуктивність читання/запису, горизонтальне масштабування та відмовостійкість. За традицією, вибір починається з категорії бази даних, SQL або NoSQL, оскільки кожна категорія надає чіткий набір компромісів. Висока продуктивність з погляду низької затримки та високої пропускної спроможності зазвичай розглядається як вимога, що не допускає компромісів, і тому є необхідною для будь-якої бази даних з вибірки.
Мета цієї статті – допомогти розробникам додатків зробити правильний вибір між SQL та NoSQL у контексті моделювання даних програми. Ми розглянемо одну SQL базу даних, а саме PostgreSQL та дві NoSQL бази даних – Cassandra та MongoDB, щоб розповісти про основи проектування баз даних, такі як створення таблиць, їх заповнення, читання даних з таблиці та їх видалення. У наступній статті ми обов'язково розглянемо індекси, транзакції, JOIN'и, директиви TTL та проектування баз даних на основі JSON.
У чому відмінність SQL від NoSQL?
SQL бази даних підвищують гнучкість програми завдяки транзакційним гарантіям ACID, а також завдяки своїй здатності вимагати дані за допомогою JOIN несподіваними способами поверх існуючих нормалізованих моделей реляційних баз даних.
Враховуючи їхню монолітну/одновузлову архітектуру та використання моделі реплікації master-slave для надмірності, традиційні SQL бази даних не мають двох важливих особливостей – лінійної масштабованості запису (тобто автоматичного поділу на кілька вузлів) та автоматичної/нульової втрати даних. Це означає, що обсяг одержуваних даних не може перевищувати максимальну пропускну запис одного вузла. Крім цього, деяка тимчасова втрата даних має бути врахована при стійкості до відмови (в архітектурі без поділу ресурсів). Тут треба мати на увазі, що нещодавні комміти ще не позначилися на підлеглій (slave) копії. Оновлення без простою також важкодоступні в SQL базах даних.
NoSQL бази даних за своєю натурою зазвичай розподілені, тобто. в них дані розбиваються на секції та розподіляються по кількох вузлах. Вони вимагають денормалізацію. Це означає, що внесені дані також мають бути скопійовані кілька разів для відповіді на конкретні запити, які ви надсилаєте. Загальна мета полягає в тому, щоб отримати високу продуктивність шляхом зменшення кількості шардів, доступних під час читання. Звідси випливає твердження, що NoSQL вимагає від вас моделювати ваші запити, а той час, як SQL вимагає моделювати ваші дані.
NoSQL акцентується на досягненні високої продуктивності в розподіленому кластері і це є основним обґрунтуванням безлічі компромісів проектування баз даних, які включають втрату транзакцій ACID, JOIN і узгоджені глобальні вторинні індекси.
Існує думка, що, хоча NoSQL бази даних і забезпечують лінійну масштабованість запису і високу стійкість до відмови, втрата транзакційних гарантій робить їх непридатними для критично важливих даних.
Наступна таблиця показує, як моделювання даних у NoSQL відрізняється від SQL.
SQL та NoSQL: Чому потрібні обидві?
На реальних додатках з великою кількістю користувачів, таких як Amazon.com, Netflix, Uber та Airbnb, лежить виконання складних розносортних завдань. Наприклад, додаток для електронної торгівлі подібний до Amazon.com повинен зберігати легковажні, високо-критичні дані, такі як інформація про користувачів, продукти, замовлення, рахунки-фактури, поряд з важкими, але менш чутливими даними, такими як огляди продуктів, повідомлення служби підтримки , активність користувачів, відгуки та рекомендації користувачів. Звичайно, ці програми покладаються принаймні на одну SQL базу даних поряд з як мінімум однією базою даних NoSQL. У міжрегіональних та глобальних системах, NoSQL база даних працює як георозподілений кеш для даних, що зберігаються в довіреному джерелі, SQL базі даних, що працює в якомусь одному регіоні.
Як YugaByte DB об'єднує SQL і NoSQL?
Побудована на лог-орієнтованому змішаному двигуні для зберігання, авто-шардингу, шардингової розподіленої консенсусної реплікації та розподілених транзакціях ACID (натхнених Google Spanner), YugaByte DB є першою у світі базою даних з відкритим вихідним кодом, яка одночасно сумісна з No & ) та SQL (PostgreSQL). Як показано в таблиці нижче, YCQL, API YugaByte DB, сумісне з Cassandra, додає поняття одно- і багатоключові ACID транзакції та глобальні вторинні індекси в API NoSQL, тим самим відкриваючи еру транзакційних NoSQL баз даних. Крім того, YCQL, API YugaByte DB сумісне з PostgreSQL, додає поняття лінійного масштабування запису та автоматичної відмовостійкості до SQL API, являючи світові розподілені SQL бази даних. Оскільки база даних YugaByte DB є транзакційною по суті, то API NoSQL тепер можна використовувати в контексті критично важливих даних.
Як раніше було сказано у статті
- Якщо основне робоче навантаження – це багатоключові операції з JOIN'ами, то при виборі YSQL розумійте, що ваші ключі можуть бути розподілені по кількох вузлах, що призведе до більш високої затримки та/або зниження пропускної спроможності, ніж у NoSQL.
- В іншому випадку виберіть будь-який з двох NoSQL API, пам'ятаючи про те, що ви отримаєте вищу продуктивність в результаті запитів, що обслуговуються з одного вузла за раз. YugaByte DB може бути єдиною операційною базою даних для реальних складних додатків, у яких необхідно керувати кількома робочими навантаженнями одночасно.
В основі лабораторії моделювання даних (Data modeling lab) у наступному розділі лежать сумісні з PostgreSQL та Cassandra API бази даних YugaByte DB на відміну від вихідних баз даних. Цей підхід підкреслює простоту взаємодії з двома різними API (на двох різних портах) одного і того ж кластера баз даних на відміну від використання повністю незалежних кластерів двох різних баз даних.
У наступних розділах ми познайомимося з лабораторією моделювання даних, щоб проілюструвати відмінність та деякі спільні риси даних даних.
Лабораторія моделювання даних
Встановлення баз даних
Враховуючи акцент на проектуванні моделі даних (а не на складних архітектурах розгортання), ми встановимо бази даних у Docker контейнери на локальному комп'ютері, а потім взаємодіятимемо з ними, використовуючи відповідні оболонки командного рядка.
Сумісна з PostgreSQL & Cassandra, база даних YugaByte DB
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
Доступ через командний рядок
Давайте підключимося до баз даних за допомогою оболонки командного рядка для відповідних API.
PostgreSQL
docker exec -it yb-postgres-n1 /home/yugabyte/postgres/bin/psql -p 5433 -U postgres
Кассандра
cqlsh
у каталозі bin
.
Зверніть увагу, що CQL був натхненний SQL і має аналогічні поняття таблиць, рядків, стовпців та індексів. Однак, як мова NoSQL, він додає певний набір обмежень, більшість з яких ми також розглянемо в інших статтях.
docker exec -it yb-tserver-n1 /home/yugabyte/bin/cqlsh
MongoDB
docker exec -it my-mongo bash
cd bin
mongo
Створення таблиці
Зараз ми можемо взаємодіяти з базою даних для виконання різних операцій за допомогою командного рядка. Почнемо зі створення таблиці, яка зберігає інформацію про пісні написані різними виконавцями. Ці пісні можуть бути частиною альбому. Також опціональні атрибути для пісні – рік випуску, ціна, жанр та рейтинг. Нам потрібно враховувати додаткові атрибути, які можуть знадобитися у майбутньому через поле «теги». Воно може зберігати напівструктуровані дані у вигляді пар ключ-значення.
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 дуже схоже на PostgreSQL. Однією з основних відмінностей є відсутність обмежень цілісності (наприклад, NOT NULL), але це входить у зону відповідальності програми, а чи не NoSQL бази даних. Первинний ключ складається з ключа розділу (стовпець Artist у прикладі нижче) та набору стовпців кластеризації (стовпець SongTitle у наведеному нижче прикладі). Ключ розділу визначає який розділ/шард помісити рядок, а стовпці кластеризації вказують, як мають бути організовані дані всередині поточного шарда.
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 організовує дані в бази даних (Database) (аналогічно Keyspace в Cassandra), де є колекції (Collections) (аналогічно таблицям), у яких лежать документи (Documents) (аналогічно рядкам таблиці). У MongoDB у принципі не потрібне визначення початкової схеми. Команда "use database", показана нижче, створює екземпляр бази даних під час першого виклику та змінює контекст для новоствореної бази даних. Навіть колекції не потрібно створювати явно, вони створюються автоматично, просто при додаванні першого документа до нової колекції. Зверніть увагу, що MongoDB за промовчанням використовує тестову базу даних, тому будь-яка операція рівня колекцій без вказівки конкретної бази буде виконуватися в ній за промовчанням.
use myNewDatabase;
Отримання інформації про таблицю
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)
Кассандра
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;
Внесення даних до таблиці
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}'
);
Кассандра
Загалом вираз INSERT
у Cassandra виглядає дуже схоже на аналогічне у PostgreSQL. Однак є одна велика різниця в семантиці. У Cassandra INSERT
фактично є операцією UPSERT
, де до рядка додаються останні значення, якщо рядок вже існує.
Введення даних відбувається аналогічно PostgreSQL
INSERT
вище
.
MongoDB
Незважаючи на те, що MongoDB є NoSQL базою даних, подібно до Cassandra, її операція внесення даних не має нічого спільного з семантичною поведінкою в Cassandra. У MongoDB UPSERT
що робить його схожим на PostgreSQL. Додавання даних за замовчуванням без _idspecified
призведе до додавання нового документа до колекції.
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"
}
}
);
Запит таблиці
Можливо, найбільш суттєва різниця між SQL та NoSQL з погляду складання запитів полягає у використанні формулювань FROM
и WHERE
. SQL дозволяє після висловлювання FROM
вибирати кілька таблиць, а вираз з WHERE
може бути будь-якої складності (включаючи операції JOIN
між таблицями). Однак NoSQL має тенденцію накладати жорстке обмеження на FROM
, і працювати тільки з однією вказаною таблицею, а в WHERE
, завжди має бути вказаний первинний ключ. Це пов'язано із прагненням до підвищення продуктивності NoSQL, про яке ми говорили раніше. Це прагнення призводить до будь-якого зменшення будь-якої крос-табличного та крос-ключового взаємодії. Воно може призвести до великої затримки в міжвузловому зв'язку при відповіді запит і, отже, його найкраще уникати у принципі. Наприклад, Cassandra вимагає, щоб запити були обмежені певними операторами (дозволено лише =, IN, <, >, =>, <=
) на ключах розділів, за винятком випадків запиту вторинного індексу (тут дозволено лише оператор =).
PostgreSQL
Далі будуть наведені три приклади запитів, які легко можуть бути виконані SQL базою даних.
- Вивести усі пісні виконавця;
- Вивести всі пісні виконавця, що збігаються з першою частиною назви;
- Вивести всі пісні виконавця, які мають певне слово у назві та мають ціну менше 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;
Кассандра
З перерахованих вище запитів PostgreSQL тільки перший працюватиме в Cassandra без змін, оскільки оператор LIKE
не можна застосовувати до стовпців кластеризації, таких як SongTitle
. У цьому випадку допускаються лише оператори =
и 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
Як показано в попередніх прикладах, основним методом створення запитів до MongoDB є music
(на прикладі нижче), тому запит за кількома колекціями заборонений.
db.music.find( {
artist: "No One You Know"
}
);
db.music.find( {
artist: "No One You Know",
songTitle: /Call/
}
);
Зчитування всіх рядків таблиці
Читання всіх рядків — це окремий випадок того шаблону запиту, який ми розглядали раніше.
PostgreSQL
SELECT *
FROM Music;
Кассандра
Аналогічно прикладу в PostgreSQL вище.
MongoDB
db.music.find( {} );
Редагування даних у таблиці
PostgreSQL
PostgreSQL надає інструкцію UPDATE
зміни даних. Вона не має можливостей UPSERT
тому виконання цієї інструкції завершиться помилкою, якщо рядка більше немає в базі даних.
UPDATE Music
SET Genre = 'Disco'
WHERE Artist = 'The Acme Band' AND SongTitle = 'Still In Love';
Кассандра
У Cassandra є UPDATE
аналогічний PostgreSQL. UPDATE
має ту ж семантику UPSERT
подібно INSERT
.
Аналогічно прикладу в PostgreSQL вище.
MongoDB
Операція UPSERT
. Оновлення кількох документів та поведінка аналогічна UPSERT
можна застосувати, встановивши додаткові прапори для операції. Як, наприклад, у наведеному нижче прикладі відбувається оновлення жанру конкретного виконавця за його піснею.
db.music.update(
{"artist": "The Acme Band"},
{
$set: {
"genre": "Disco"
}
},
{"multi": true, "upsert": true}
);
Видалення даних із таблиці
PostgreSQL
DELETE FROM Music
WHERE Artist = 'The Acme Band' AND SongTitle = 'Look Out, World';
Кассандра
Аналогічно прикладу в PostgreSQL вище.
MongoDB
У MongoDB є два типи операцій для видалення документів.
db.music.deleteMany( {
artist: "The Acme Band"
}
);
Видалення таблиці
PostgreSQL
DROP TABLE Music;
Кассандра
Аналогічно прикладу в PostgreSQL вище.
MongoDB
db.music.drop();
Висновок
Суперечки про вибір між SQL та NoSQL вирують вже понад 10 років. Є два основні аспекти цієї суперечки: архітектура ядра бази даних (монолітний, транзакційний SQL проти розподіленого, нетранзакційного NoSQL) та підхід до проектування бази даних (моделювання даних у SQL проти моделювання ваших запитів у NoSQL).
З розподіленою транзакційною базою даних, такою як YugaByte DB, дебати щодо архітектури бази даних можуть бути легко розвіяні. У міру того, як обсяги даних стають більшими, ніж те, що може бути записано в один вузол, повністю розподілена архітектура, яка підтримує лінійну масштабованість запису з автоматичним шардингом/ребалансуванням, стає необхідною.
Крім того, як сказано в одній із статей
Повертаючись до обговорення проектування баз даних, справедливо сказати, що обидва підходи до проектування (SQL та NoSQL) необхідні для будь-якої складної реальної програми. Підхід SQL «моделювання даних» дозволяє розробникам легше задовольняти мінливі бізнес-вимоги, у той час як підхід NoSQL «моделювання запитів» дозволяє тим же розробникам оперувати великими обсягами даних, маючи невелику затримку та високу пропускну здатність. Саме з цієї причини YugaByte DB надає SQL та NoSQL API у загальному ядрі, а не пропагує якийсь один із підходів. Крім того, забезпечуючи сумісність із популярними мовами баз даних, включаючи PostgreSQL та Cassandra, YugaByte DB гарантує, що розробникам не доведеться вивчати іншу мову, щоб працювати з розподіленим строго узгодженим ядром бази даних.
У цій статті ми розібралися, як основи проектування баз даних різняться у PostgreSQL, Cassandra та MongoDB. У наступних статтях ми поринемо у передові концепції проектування, такі як індекси, транзакції, JOIN'и, директиви TTL та JSON-документи.
Бажаємо вам відмінно провести вихідні, що залишилися, і запрошуємо на
Джерело: habr.com